Jump to page: 1 2
Thread overview
Yet another leak in the sinking ship of @safe
Feb 18, 2016
H. S. Teoh
Feb 18, 2016
Timon Gehr
Feb 18, 2016
Timon Gehr
Feb 18, 2016
Jonathan M Davis
Feb 18, 2016
H. S. Teoh
Feb 18, 2016
Jonathan M Davis
Feb 18, 2016
H. S. Teoh
Feb 18, 2016
Jonathan M Davis
Feb 18, 2016
Era Scarecrow
Feb 18, 2016
Timon Gehr
Feb 18, 2016
Chris Wright
Feb 18, 2016
Adam D. Ruppe
Feb 18, 2016
Kagamin
Feb 18, 2016
Kagamin
Feb 18, 2016
Jonathan M Davis
Feb 18, 2016
H. S. Teoh
February 18, 2016
While @safe is a good idea in principle, the current implementation is rather lackluster. Consider, for example:

	void readData(void[] buffer) @safe
	{
		ubyte[] buf = cast(ubyte[]) buffer;
		buf[0] = 0xFF;
	}
	void main() @safe
	{
		auto buffer = new Object[1];
		readData(buffer);
	}

There are (at least) two major problems here:

1) Casting an array of elements with indirections (in this case Object[]) to void[] is questionable at best, outright unsafe at worst, as shown here. Even if we were to rewrite readData() and mark it @trusted, it still raises the question of what a @trusted function can legally do with void[], which is essentially a type-erased array, that justifies being tagged as @trusted.  How can a function do anything that doesn't break @safety if all type information about the array has been erased, and it essentially sees it only as a ubyte[]?  I'm inclined to say that @trusted functions should only be allowed to receive const(void)[] as parameter, not void[].

	https://issues.dlang.org/show_bug.cgi?id=15702

2) To add salt to the wound, though, blatantly casting void[] to T[] is allowed in @safe code, thus, readData() can even get away with claiming to be @safe, when in fact it is anything but.

	https://issues.dlang.org/show_bug.cgi?id=15672

These two issues basically make the @safe annotation an empty promise, because *anything* could happen entirely within the realm of @safe code, and memory safety isn't guaranteed at all.  It's bad enough that some Phobos modules (*ahem*std.socket*cough*) liberally sprinkle @trusted on every function without regard to whether it's truly justified (e.g., see https://issues.dlang.org/show_bug.cgi?id=15672), now even if we exclude @trusted functions from the mix, we can still break @safe without the compiler even raising an eyebrow.

Sadly, the above issues are merely the tip of the iceberg. There are many more holes in @safe, such as unions containing pointers, unions of @safe and @system delegates, unaligned pointers, void initialization of members with indirection, just to name a few.  If we want the @safe ship to float, we have a lot of work cut out for us.


--T
February 18, 2016
On 18.02.2016 17:37, H. S. Teoh via Digitalmars-d wrote:
> While @safe is a good idea in principle, the current implementation is
> rather lackluster. Consider, for example:
>
> 	void readData(void[] buffer) @safe
> 	{
> 		ubyte[] buf = cast(ubyte[]) buffer;
> 		buf[0] = 0xFF;
> 	}
> 	void main() @safe
> 	{
> 		auto buffer = new Object[1];
> 		readData(buffer);
> 	}
>
> There are (at least) two major problems here:
>
> 1) Casting an array of elements with indirections (in this case
> Object[]) to void[] is questionable at best, outright unsafe at worst,
> as shown here. Even if we were to rewrite readData() and mark it
> @trusted, it still raises the question of what a @trusted function can
> legally do with void[], which is essentially a type-erased array, that
> justifies being tagged as @trusted.  How can a function do anything that
> doesn't break @safety if all type information about the array has been
> erased, and it essentially sees it only as a ubyte[]?  I'm inclined to
> say that @trusted functions should only be allowed to receive
> const(void)[] as parameter, not void[].
>
> 	https://issues.dlang.org/show_bug.cgi?id=15702
>

No problem here. There is no way to assign to a void[] without doing 2.


> 2) To add salt to the wound, though, blatantly casting void[] to T[] is
> allowed in @safe code, thus, readData() can even get away with claiming
> to be @safe, when in fact it is anything but.
>
> 	https://issues.dlang.org/show_bug.cgi?id=15672

This is the culprit.
February 18, 2016
On 2/18/16 1:30 PM, Timon Gehr wrote:
> On 18.02.2016 17:37, H. S. Teoh via Digitalmars-d wrote:
>> While @safe is a good idea in principle, the current implementation is
>> rather lackluster. Consider, for example:
>>
>>     void readData(void[] buffer) @safe
>>     {
>>         ubyte[] buf = cast(ubyte[]) buffer;
>>         buf[0] = 0xFF;
>>     }
>>     void main() @safe
>>     {
>>         auto buffer = new Object[1];
>>         readData(buffer);
>>     }
>>
>> There are (at least) two major problems here:
>>
>> 1) Casting an array of elements with indirections (in this case
>> Object[]) to void[] is questionable at best, outright unsafe at worst,
>> as shown here. Even if we were to rewrite readData() and mark it
>> @trusted, it still raises the question of what a @trusted function can
>> legally do with void[], which is essentially a type-erased array, that
>> justifies being tagged as @trusted.  How can a function do anything that
>> doesn't break @safety if all type information about the array has been
>> erased, and it essentially sees it only as a ubyte[]?  I'm inclined to
>> say that @trusted functions should only be allowed to receive
>> const(void)[] as parameter, not void[].
>>
>>     https://issues.dlang.org/show_bug.cgi?id=15702
>>
>
> No problem here. There is no way to assign to a void[] without doing 2.

foo(void[] arr)
{
   void[] arr2 = [1234, 5678, 91011];
   arr[] = arr2[0 .. arr.length];
}

-Steve
February 18, 2016
On 18.02.2016 19:41, Steven Schveighoffer wrote:
> On 2/18/16 1:30 PM, Timon Gehr wrote:
>> On 18.02.2016 17:37, H. S. Teoh via Digitalmars-d wrote:
>>> While @safe is a good idea in principle, the current implementation is
>>> rather lackluster. Consider, for example:
>>>
>>>     void readData(void[] buffer) @safe
>>>     {
>>>         ubyte[] buf = cast(ubyte[]) buffer;
>>>         buf[0] = 0xFF;
>>>     }
>>>     void main() @safe
>>>     {
>>>         auto buffer = new Object[1];
>>>         readData(buffer);
>>>     }
>>>
>>> There are (at least) two major problems here:
>>>
>>> 1) Casting an array of elements with indirections (in this case
>>> Object[]) to void[] is questionable at best, outright unsafe at worst,
>>> as shown here. Even if we were to rewrite readData() and mark it
>>> @trusted, it still raises the question of what a @trusted function can
>>> legally do with void[], which is essentially a type-erased array, that
>>> justifies being tagged as @trusted.  How can a function do anything that
>>> doesn't break @safety if all type information about the array has been
>>> erased, and it essentially sees it only as a ubyte[]?  I'm inclined to
>>> say that @trusted functions should only be allowed to receive
>>> const(void)[] as parameter, not void[].
>>>
>>>     https://issues.dlang.org/show_bug.cgi?id=15702
>>>
>>
>> No problem here. There is no way to assign to a void[] without doing 2.
>
> foo(void[] arr)
> {
>     void[] arr2 = [1234, 5678, 91011];
>     arr[] = arr2[0 .. arr.length];
> }
>
> -Steve

Good point. :)
February 18, 2016
On Thursday, 18 February 2016 at 18:41:25 UTC, Steven Schveighoffer wrote:
> On 2/18/16 1:30 PM, Timon Gehr wrote:
>> On 18.02.2016 17:37, H. S. Teoh via Digitalmars-d wrote:
>>> While @safe is a good idea in principle, the current implementation is
>>> rather lackluster. Consider, for example:
>>>
>>>     void readData(void[] buffer) @safe
>>>     {
>>>         ubyte[] buf = cast(ubyte[]) buffer;
>>>         buf[0] = 0xFF;
>>>     }
>>>     void main() @safe
>>>     {
>>>         auto buffer = new Object[1];
>>>         readData(buffer);
>>>     }
>>>
>>> There are (at least) two major problems here:
>>>
>>> 1) Casting an array of elements with indirections (in this case
>>> Object[]) to void[] is questionable at best, outright unsafe at worst,
>>> as shown here. Even if we were to rewrite readData() and mark it
>>> @trusted, it still raises the question of what a @trusted function can
>>> legally do with void[], which is essentially a type-erased array, that
>>> justifies being tagged as @trusted.  How can a function do anything that
>>> doesn't break @safety if all type information about the array has been
>>> erased, and it essentially sees it only as a ubyte[]?  I'm inclined to
>>> say that @trusted functions should only be allowed to receive
>>> const(void)[] as parameter, not void[].
>>>
>>>     https://issues.dlang.org/show_bug.cgi?id=15702
>>>
>>
>> No problem here. There is no way to assign to a void[] without doing 2.
>
> foo(void[] arr)
> {
>    void[] arr2 = [1234, 5678, 91011];
>    arr[] = arr2[0 .. arr.length];
> }

Well, I'm not sure that that's actually not @safe. It's trying to interpret the void[] that's the problem. Certainly, you can convert T[] to void[] and pass it around all day without risking any memory corruption, so that should definitely be @safe, and I don't see how reducing the length of a void[] could actually cause memory corruption on its own. It's when you cast the void[] to something else that you risk things going south, and that's what needs to be @system. So, I'm not sure that there's actually any reason for your example code to not be @safe.

- Jonathan M Davis
February 18, 2016
On 2/18/16 11:37 AM, H. S. Teoh via Digitalmars-d wrote:
> While @safe is a good idea in principle, the current implementation is
> rather lackluster.

IMO, I think safe code should disallow casting that doesn't involve a runtime function that verifies safety (such as casting an object to another type, or invoking a @safe opCast), including casting array types.

And implicit casts to void[] should be disallowed.

This would be a painful restriction, which is why I think it won't happen :(

-Steve
February 18, 2016
On Thu, Feb 18, 2016 at 06:50:34PM +0000, Jonathan M Davis via Digitalmars-d wrote:
> On Thursday, 18 February 2016 at 18:41:25 UTC, Steven Schveighoffer wrote:
[...]
> >foo(void[] arr)
> >{
> >   void[] arr2 = [1234, 5678, 91011];
> >   arr[] = arr2[0 .. arr.length];
> >}
> 
> Well, I'm not sure that that's actually not @safe.

How can it possibly be @safe??? Consider:

	void main() @safe {
		Object[] objs = [ new Object() ];
		foo(objs);
	}

Now the pointer to the Object instance has been corrupted.


> It's trying to interpret the void[] that's the problem. Certainly, you can convert T[] to void[] and pass it around all day without risking any memory corruption, so that should definitely be @safe, and I don't see how reducing the length of a void[] could actually cause memory corruption on its own. It's when you cast the void[] to something else that you risk things going south, and that's what needs to be @system. So, I'm not sure that there's actually any reason for your example code to not be @safe.
[...]

I think you missed the point of his example. :-) The point is that it's
perfectly legal to (1) cast an array of int to void[], and (2) it's also
perfectly legal to cast an array of anything to void[], and (3) under
current rules, it's perfectly legal to copy one void[] to another
void[].

Arguably, (3) should not be allowed in @safe code. Which again brings us back to the point, that if any function takes void[] as an argument, is there *anything* it can do with the void[] other than reading it, that *won't* break @safe?

If there's *nothing* it can legally do with a void[] (other than reading it) without violating @safe, then shouldn't it be a requirement that all functions marked @safe must take const(void)[] rather than void[]?


T

-- 
Chance favours the prepared mind. -- Louis Pasteur
February 18, 2016
On 2/18/16 1:50 PM, Jonathan M Davis wrote:
> On Thursday, 18 February 2016 at 18:41:25 UTC, Steven Schveighoffer wrote:
>> foo(void[] arr)
>> {
>>    void[] arr2 = [1234, 5678, 91011];
>>    arr[] = arr2[0 .. arr.length];
>> }
>
> Well, I'm not sure that that's actually not @safe. It's trying to
> interpret the void[] that's the problem. Certainly, you can convert T[]
> to void[] and pass it around all day without risking any memory
> corruption, so that should definitely be @safe, and I don't see how
> reducing the length of a void[] could actually cause memory corruption
> on its own. It's when you cast the void[] to something else that you
> risk things going south, and that's what needs to be @system. So, I'm
> not sure that there's actually any reason for your example code to not
> be @safe.

You don't need to cast to use the original.

void bar() @safe
{
   int *[] ptrs = new int*[1];
   foo(ptrs);
   *ptrs[0] = 5;
}

-Steve
February 18, 2016
On Thursday, 18 February 2016 at 18:41:25 UTC, Steven Schveighoffer wrote:
> On 2/18/16 1:30 PM, Timon Gehr wrote:
>> No problem here. There is no way to assign to a void[] without doing 2.
>
> foo(void[] arr)
> {
>    void[] arr2 = [1234, 5678, 91011];
>    arr[] = arr2[0 .. arr.length];
> }

 Since void throws away type information (and all the safety related to it), would it be easier to simply require @safe code can't cast implicitly to void? It seems like explicit casting would take care of most of this, or disallowing to/from void converting period while in @safe code.

 To be honest, I think there's only 1 time I actually used a void[] in my code, and that was while writing a section in the BitArray replacement code years back in the case you wanted to use/read another block of data as the source for the BitArray. Beyond that I never touched it.
February 18, 2016
On Thursday, 18 February 2016 at 18:58:56 UTC, H. S. Teoh wrote:
> On Thu, Feb 18, 2016 at 06:50:34PM +0000, Jonathan M Davis via Digitalmars-d wrote:
>> On Thursday, 18 February 2016 at 18:41:25 UTC, Steven Schveighoffer wrote:
> [...]
>> >foo(void[] arr)
>> >{
>> >   void[] arr2 = [1234, 5678, 91011];
>> >   arr[] = arr2[0 .. arr.length];
>> >}
>> 
>> Well, I'm not sure that that's actually not @safe.
>
> How can it possibly be @safe??? Consider:
>
> 	void main() @safe {
> 		Object[] objs = [ new Object() ];
> 		foo(objs);
> 	}
>
> Now the pointer to the Object instance has been corrupted.

Does it matter what state the void[] is in until you actually attempt to cast it to something else? If you have

Object[] oArr = [ new Object ];
void[] vArr = oArr;
vArr = vArr[0 .. $ - 1];

it's not like that's going to cause any problems - not by itself. As soon as you convert it back to Object[] or to int[] or whatever, _then_ you're screwed. But converting from void[] to anything is inherently @system regardless of what you did to the array or where it came from. Now, given that reducing the length like that is setting you up for a bad conversion, I can certainly see an argument for reducing the length of a void[] being @system, but in and of itself, it isn't going to corrupt memory. It just sets the stage for it. As long as it stays void[], everything is fine.

Now given that you can't possibly do anything useful with reducing the length of void[] and that it does make it so that future, @system operations would corrupt memory, I can certainly see making it @system, but I dispute that it's really doing anything unsafe on its own.

>> It's trying to interpret the void[] that's the problem. Certainly, you can convert T[] to void[] and pass it around all day without risking any memory corruption, so that should definitely be @safe, and I don't see how reducing the length of a void[] could actually cause memory corruption on its own. It's when you cast the void[] to something else that you risk things going south, and that's what needs to be @system. So, I'm not sure that there's actually any reason for your example code to not be @safe.
> [...]
>
> I think you missed the point of his example. :-) The point is that it's
> perfectly legal to (1) cast an array of int to void[], and (2) it's also
> perfectly legal to cast an array of anything to void[], and (3) under
> current rules, it's perfectly legal to copy one void[] to another
> void[].
>
> Arguably, (3) should not be allowed in @safe code. Which again brings us back to the point, that if any function takes void[] as an argument, is there *anything* it can do with the void[] other than reading it, that *won't* break @safe?
>
> If there's *nothing* it can legally do with a void[] (other than reading it) without violating @safe, then shouldn't it be a requirement that all functions marked @safe must take const(void)[] rather than void[]?

It could pass it to something else without actually doing anything to it, in which case, I don't see why it couldn't be @safe. It's interpreting a void[] that's the problem. _That_ needs to be @system. Merely converting to void[] or passing it around is harmless. And as long as converting void[] to anything else is considered @system like it should be, I don't see why it would be necessary to care about whether a function accepts void[] or not when considering @safety. Useless as it may be

    void[] identity(void[] arr) @safe
    {
        return arr;
    }

is perfectly @safe. If the compiler does its job, and the function does cast the void[], then the function will be considered @system unless it's marked @trusted, and all is well without caring one whit about how the function was passed void[].

The problem with std.socket is not that it accepts void[] and considers that @safe; it's that it passes that void[] to something else, treating that as @safe, when it's not. And since it's a C function that's being called, and it accesses the ptr member of the void[], it's already @system, forcing @trusted to be used to make the compiler happy. So, the problem is that @trusted was used when it shouldn't have been, not that the type system allowed void[] to be passed in without marking it as @system. And since the function was marked @trusted, it's not like having the compiler treat a function that accepted void[] as @system would have helped anyway.

- Jonathan M Davis
« First   ‹ Prev
1 2