| Thread overview | |||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
February 18, 2016 Yet another leak in the sinking ship of @safe | ||||
|---|---|---|---|---|
| ||||
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 Re: Yet another leak in the sinking ship of @safe | ||||
|---|---|---|---|---|
| ||||
Posted in reply to H. S. Teoh | 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 Re: Yet another leak in the sinking ship of @safe | ||||
|---|---|---|---|---|
| ||||
Posted in reply to Timon Gehr | 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 Re: Yet another leak in the sinking ship of @safe | ||||
|---|---|---|---|---|
| ||||
Posted in reply to Steven Schveighoffer | 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 Re: Yet another leak in the sinking ship of @safe | ||||
|---|---|---|---|---|
| ||||
Posted in reply to Steven Schveighoffer | 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 Re: Yet another leak in the sinking ship of @safe | ||||
|---|---|---|---|---|
| ||||
Posted in reply to H. S. Teoh | 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 Re: Yet another leak in the sinking ship of @safe | ||||
|---|---|---|---|---|
| ||||
Posted in reply to Jonathan M Davis | 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 Re: Yet another leak in the sinking ship of @safe | ||||
|---|---|---|---|---|
| ||||
Posted in reply to Jonathan M Davis | 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 Re: Yet another leak in the sinking ship of @safe | ||||
|---|---|---|---|---|
| ||||
Posted in reply to Steven Schveighoffer | 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 Re: Yet another leak in the sinking ship of @safe | ||||
|---|---|---|---|---|
| ||||
Posted in reply to H. S. Teoh | 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 | |||
Copyright © 1999-2021 by the D Language Foundation
Permalink
Reply