Thread overview | ||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
January 20, 2021 [Issue 21565] @safe code allows modification of a scalar that overlaps with a pointer | ||||
---|---|---|---|---|
| ||||
https://issues.dlang.org/show_bug.cgi?id=21565 ag0aep6g <ag0aep6g@gmail.com> changed: What |Removed |Added ---------------------------------------------------------------------------- CC| |ag0aep6g@gmail.com --- Comment #1 from ag0aep6g <ag0aep6g@gmail.com> --- (In reply to Steven Schveighoffer from comment #0) > Such access should be considered illegal as it does not fit into any of the categories. 1, 2, 4, 5, and 6 trivially do not apply. Whether int * is considered a "basic type" is possibly open to interpretation, but I would say it is not, considering that if it were, then rule 2 would allow arbitrary pointer usage. This disqualifies 3, or at least suggests an edit is in order. Just clarifying one little thing: `int*` is definitely not a "basic data type". As a pointer, it's a "derived data type". Definitions are here: <https://dlang.org/spec/type.html>. -- |
January 20, 2021 [Issue 21565] @safe code allows modification of a scalar that overlaps with a pointer | ||||
---|---|---|---|---|
| ||||
https://issues.dlang.org/show_bug.cgi?id=21565 Paul Backus <snarwin+bugzilla@gmail.com> changed: What |Removed |Added ---------------------------------------------------------------------------- CC| |snarwin+bugzilla@gmail.com --- Comment #2 from Paul Backus <snarwin+bugzilla@gmail.com> --- > One might suggest that there is no harm in allowing mutating a scalar that overlaps with a pointer if the pointer cannot be accessed. But this is a naive view of code. Not all code is @safe, and if @trusted code cannot be reasoned about without also having to manually verify all @safe code, then there is no point to @trusted code. I think there are two possible interpretations here. Background: @trusted code is permitted by the language spec to assume its arguments are free from unsafe aliasing and unsafe values [1], so anything that allows unsafe values or unsafe aliasing to be created in @safe code is a bug in @safe, not in any particular piece of @trusted code. The question is: should the value of `t` after `t.x = 5`, in comment 1's example, be considered an unsafe value? If we go by the definition in the spec, the answer is clearly "yes": > A struct/union instance is safe when: > > * the values of its accessible fields are safe, and > * it does not introduce unsafe aliasing with unions. The union type `T` overlaps an integer and a pointer, so *any* value of type `T` is automatically unsafe. It follows from this interpretation that we should not be allowed to use `T` in @safe code *at all*, and that *any* @trusted function that receives an argument of type `T` is allowed to cause undefined behavior, regardless of that argument's value. This is probably not the conclusion we wanted to end up at, so let's try again. If we amend the spec as follows: > A struct/union instance is safe when: > > * the values of its accessible fields are safe, and > * it does not introduce unsafe aliasing with unions **that is accessible > from @safe code**. ...then `t`'s value becomes safe, and we are allowed to use it in @safe and @trusted code as long as we are careful not to let @safe code access `t.x` and `t.y` at the same time. I think this interpretation is much more useful, and almost certainly the intended one, so I suggest that this is really a bug in the spec, not the implementation. [1] https://dlang.org/spec/function.html#safe-interfaces -- |
January 20, 2021 [Issue 21565] @safe code allows modification of a scalar that overlaps with a pointer | ||||
---|---|---|---|---|
| ||||
https://issues.dlang.org/show_bug.cgi?id=21565 --- Comment #3 from Steven Schveighoffer <schveiguy@gmail.com> --- A union between a pointer and integer is most definitely unsafe in all instances. If you never intend to access the int*, in any circumstance, then why have a union? If you do intend to access the int *, then having any safe code anywhere just change the integer ruins the any safety assumptions that the @trusted or @system code can make. Essentially, it means @trusted code can never access such a union reliably except to access just the integer. This means that T is OK to use ONLY in @system code, or ONLY in @safe code, but NEVER in @trusted code (unless you just follow the @safe rules). I don't feel like we should bend the spec over backwards to fit with the implementation, when there isn't really a benefit (other than being able to close a bug report). -- |
January 21, 2021 [Issue 21565] @safe code allows modification of a scalar that overlaps with a pointer | ||||
---|---|---|---|---|
| ||||
https://issues.dlang.org/show_bug.cgi?id=21565 --- Comment #4 from Paul Backus <snarwin+bugzilla@gmail.com> --- Consider the following example: --- union T { int x; int* y; } @trusted void example(T t) { import std.stdio; t.x = 123; writeln(t.x); t.y = new int; writeln(t.y); } --- This code is memory-safe. It contains no undefined behavior. Any @safe function can call this code with any possible value of `t`, and it will not corrupt memory. It also accesses both members of `t` and would not compile if annotated with @safe (i.e., it does not "follow the @safe rules"). The *intent* of the spec is clearly to allow code like this to be marked as @trusted. If the current wording of the spec does not allow that, then the spec's wording does not match its intent, and the wording should be changed. -- |
January 21, 2021 [Issue 21565] @safe code allows modification of a scalar that overlaps with a pointer | ||||
---|---|---|---|---|
| ||||
https://issues.dlang.org/show_bug.cgi?id=21565 RazvanN <razvan.nitu1305@gmail.com> changed: What |Removed |Added ---------------------------------------------------------------------------- CC| |razvan.nitu1305@gmail.com --- Comment #5 from RazvanN <razvan.nitu1305@gmail.com> --- (In reply to Steven Schveighoffer from comment #3) > A union between a pointer and integer is most definitely unsafe in all instances. If you never intend to access the int*, in any circumstance, then why have a union? It may be safe if the user sets the integer part with valid memory addresses. However, the compiler cannot know that. > If you do intend to access the int *, then having any safe code anywhere just change the integer ruins the any safety assumptions that the @trusted or @system code can make. Essentially, it means @trusted code can never access such a union reliably except to access just the integer. Trusted does not offer any guarantees. You can do whatever you want there. If you want to access a pointer that is overlapped with an integer that is the users' problem not the typesystems'. You cannot assume anything with regards to that pointer, that is the reason why it is not allowed in @safe code. In case you do use and you have a segfault, then the developer will have to audit the trusted blocks, not the @safe ones. > This means that T is OK to use ONLY in @system code, or ONLY in @safe code, but NEVER in @trusted code (unless you just follow the @safe rules). > > I don't feel like we should bend the spec over backwards to fit with the implementation, when there isn't really a benefit (other than being able to close a bug report). I think that this is an issue were reasonable people may disagree, but the fact is that @safe is checked with regards to operations not data structures. There is no concept of @safe union or @system union in D. It is the way you use it that makes it @safe/@system. From this point of view, setting an integer that is overlapped with a pointer is not unsafe, however accessing a pointer that is overlapped with an integer is. -- |
January 21, 2021 [Issue 21565] @safe code allows modification of a scalar that overlaps with a pointer | ||||
---|---|---|---|---|
| ||||
https://issues.dlang.org/show_bug.cgi?id=21565 --- Comment #6 from ag0aep6g <ag0aep6g@gmail.com> --- (In reply to Paul Backus from comment #2) > The question is: should the value of `t` after `t.x = 5`, in comment 1's example, be considered an unsafe value? [...] > If we amend the spec as follows: > > > A struct/union instance is safe when: > > > > * the values of its accessible fields are safe, and > > * it does not introduce unsafe aliasing with unions **that is accessible > > from @safe code**. > > ...then `t`'s value becomes safe, and we are allowed to use it in @safe and @trusted code as long as we are careful not to let @safe code access `t.x` and `t.y` at the same time. > > I think this interpretation is much more useful, and almost certainly the intended one, so I suggest that this is really a bug in the spec, not the implementation. > > [1] https://dlang.org/spec/function.html#safe-interfaces I can say that I did not intend that interpretation when I added "safe aliasing" to the spec. But I might have been overly conservative. Currently, the spec explicitly says that @safe code "Cannot access unions that have pointers or references overlapping with other types."[1] If that's so, then I guess it doesn't matter whether the union's aliasing is safe or not, because @safe code can't access it at all. And I guess it's still okay even if we change that to allow access to the non-pointy bits of the union, because the @safe code just sees another harmless int (or whatever). As far as I can tell, that means all possible union values can be considered safe (just like all ints are safe), and we can remove the parts about "[introducing] unsafe aliasing with unions" from the "save values" section. One must still be careful not to expose the unsafe aliasing to @safe code in other ways, but that's already covered by the spec. I wouldn't have designed it this way, but it seems to be what DMD is going for. [1] https://dlang.org/spec/function.html#safe-functions -- |
January 21, 2021 [Issue 21565] @safe code allows modification of a scalar that overlaps with a pointer | ||||
---|---|---|---|---|
| ||||
https://issues.dlang.org/show_bug.cgi?id=21565 --- Comment #7 from Steven Schveighoffer <schveiguy@gmail.com> --- (In reply to Paul Backus from comment #4) > The *intent* of the spec is clearly to allow code like this to be marked as @trusted. If the current wording of the spec does not allow that, then the spec's wording does not match its intent, and the wording should be changed. I'm not disagreeing with the requirement that system/trusted code should be needed to access aliased values. I'm disagreeing with the ability of safe code to access any part of this. And the spec currently says that. Consider this function: void example(ref T t) @trusted; This function has to assume that t only is valid as an integer, never as a pointer. Because safe code can *only* access and/or mutate the integer. In that case, what's the point of the union? Even in your example, you simply ignore the parameter (it might as well not be there). Not only that, but even if it sets the pointer in t, it must be automatically assumed once the function ends that the pointer value is no longer valid (it went back into safe-land where the code can happily mutate anything it wants in t). The union becomes an unnecessarily complicated integer. the current rules are sound, just nonsensical. It makes such unions pointless when writing safe code. -- |
January 21, 2021 [Issue 21565] @safe code allows modification of a scalar that overlaps with a pointer | ||||
---|---|---|---|---|
| ||||
https://issues.dlang.org/show_bug.cgi?id=21565 --- Comment #8 from Steven Schveighoffer <schveiguy@gmail.com> --- (In reply to RazvanN from comment #5) > (In reply to Steven Schveighoffer from comment #3) > > If you do intend to access the int *, then having any safe code anywhere just change the integer ruins the any safety assumptions that the @trusted or @system code can make. Essentially, it means @trusted code can never access such a union reliably except to access just the integer. > > Trusted does not offer any guarantees. This is why I said "assumptions" and not "guarantees". It's in fact impossible for a trusted function to guarantee the union state between calls, because safe code can do anything it wants with the union so long as there is a mutable basic type involved. > You can do whatever you want there. > If you want to access a pointer that is overlapped with an integer that is > the users' problem not the typesystems'. You cannot assume anything with > regards to that pointer, that is the reason why it is not allowed in @safe > code. In case you do use and you have a segfault, then the developer will > have > to audit the trusted blocks, not the @safe ones. Auditing is not possible. The trusted code cannot make any assumptions about the union, it must ALWAYS treat it as an integer (at least when it comes in). Inside the function, sure, it can assign things. But as soon as it leaves the function, it must be treated as an integer again. This means, such unions serve no purpose as parameters to trusted code. Ever. In which case, using such a union in safe code is pointless. It's more productive to follow the rules already set in the spec. > I think that this is an issue were reasonable people may disagree, but the fact is that @safe is checked with regards to operations not data structures. There is no concept of @safe union or @system union in D. It is the way you use it that makes it @safe/@system. From this point of view, setting an integer that is overlapped with a pointer is not unsafe, however accessing a pointer that is overlapped with an integer is. This is a misunderstanding. @safe has everything to do with data structures, and the semantics surrounding those data structures. If we don't have data rules for @safe code, then @trusted code cannot make any reasonable assumptions about the incoming parameters. Remember that @trusted code MUST ASSUME it is being called from @safe code. For example, @safe ascribes a semantic meaning to the length field of an array. It means "all the items that are pointed to by the pointer that are up to that length are accessible." Without that, arrays of data could not be passed to a @trusted function. We also have rules for an incoming pointer, which means, it only can point at a single valid value, or null. Using these rules, one can write useful @trusted code. Without such rules, either trusted code would have to assume any incoming parameters are suspect, or we have to review all code including @safe code. With a rule of "Incoming union values can only ever have scalar values set" is unnecessarily limiting. If you can only set the scalar values, why have a union that is usable in safe code that has pointer and scalar values mixed? It's what makes the DIP1035 proposal so promising -- you can ascribe your own semantic rules to types that the compiler doesn't have built in. -- |
January 21, 2021 [Issue 21565] @safe code allows modification of a scalar that overlaps with a pointer | ||||
---|---|---|---|---|
| ||||
https://issues.dlang.org/show_bug.cgi?id=21565 --- Comment #9 from Paul Backus <snarwin+bugzilla@gmail.com> --- > I'm disagreeing with the ability of safe code to access any part of this. On what grounds? The point of @safe is to prevent undefined behavior, and allowing access to the integer cannot possibly lead to undefined behavior, because all integer values are safe values. > the current rules are sound, just nonsensical. It makes such unions pointless when writing safe code. I agree--which is why I would like to replace them with rules that are both sound *and* sensical. Can we agree that that's a desirable goal? -- |
January 21, 2021 [Issue 21565] @safe code allows modification of a scalar that overlaps with a pointer | ||||
---|---|---|---|---|
| ||||
https://issues.dlang.org/show_bug.cgi?id=21565 --- Comment #10 from Steven Schveighoffer <schveiguy@gmail.com> --- (In reply to Paul Backus from comment #9) > > I'm disagreeing with the ability of safe code to access any part of this. > > On what grounds? The point of @safe is to prevent undefined behavior, and allowing access to the integer cannot possibly lead to undefined behavior, because all integer values are safe values. Read-only access is fine. Write access is not. > > > the current rules are sound, just nonsensical. It makes such unions pointless when writing safe code. > > I agree--which is why I would like to replace them with rules that are both sound *and* sensical. Can we agree that that's a desirable goal? I can't say no to the agreement ;) I just don't know what the definition of "sensical" means, based on your prior messages. What rules do you have in mind? -- |
Copyright © 1999-2021 by the D Language Foundation