-
-
Notifications
You must be signed in to change notification settings - Fork 667
fix #20508 escaping sliced stack arrays not detected #22207
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Conversation
|
Thanks for your pull request, @WalterBright! Bugzilla referencesYour PR doesn't reference any Bugzilla issue. If your PR contains non-trivial changes, please reference a Bugzilla issue or create a manual changelog.
|
Anyone know anything about this code? |
a38cd05 to
0793197
Compare
|
@dkorpel may need to enable this with an |
0793197 to
7ebdca1
Compare
If you guard the code with |
It looks like it incorrectly marks the allocator parameter as |
compiler/src/dmd/escape.d
Outdated
| if (va && !(vaIsFirstRef && v.isReturn()) && va.enclosesLifetimeOf(v)) | ||
| { | ||
| if (sc.setUnsafeDIP1000(gag, ae.loc, "assigning address of variable `%s` to `%s` with longer lifetime", v, va)) | ||
| if (setUnsafe(&sc, gag, ae.loc, "assigning address of variable `%s` to `%s` with longer lifetime", v, va)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is the wrong place to change. This still enables escaping stack allocated slices through other means than return, and if you were to perform this change consistently you are basically just enabling DIP1000 by default.
Without dip1000, slicing a static array a[] should be treated the same as taking the address of a local &a as far as @safe is concerned, and taking the address of locals is prevented by the checkAddressVar function called from AddrExp::semantic. The same could be done with SliceExp::semantic:
--- a/compiler/src/dmd/expressionsem.d
+++ b/compiler/src/dmd/expressionsem.d
@@ -10954,6 +10954,17 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
}
else if (t1b.ty == Tsarray)
{
+ if (auto ve = exp.e1.isVarExp())
+ {
+ if (auto vd = ve.var.isVarDeclaration())
+ {
+ if (!checkAddressVar(sc, exp, vd))
+ {
+ result = ErrorExp.get();
+ return;
+ }
+ }
+ }
}
else if (t1b.ty == Ttuple)
{
@@ -17649,7 +17660,7 @@ Expression modifiableLvalue(Expression _this, Scope* sc, Expression eorig = nullOf course this is a massive breaking change so it needs to be guarded by an edition.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
BTW DIP1000 is currently enabled for the 2024 edition, so it catches escaping slices anyway.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I appreciate your ideas here. But I have a bit of a different take. As you explained, I put the check on the return. (I also put it on an assignment to a global.) Returning a pointer to the stack is always an error (detected or not). That's why there is value in not requiring dip1000 to check it. But taking a slice of a stack array is legitimate (I use it often as a temporary buffer.
The compiler already attaches scope to any pointer or slice of a stack variable. Making use of that does not require any annotations nor inference of function interfaces, which were the problems with dip1000.
Returning a pointer to the stack is always a bug in the code, and I have no qualms about checking for this without adding the noted difficulties of dip1000. Yes, this doesn't detect everything dip1000 does, but I don't see a downside to adding these checks without requiring dip1000.
I'd like to see what more we can do without the problems of dip1000.
I was a bit nervous about what failures we'd encounter with this PR, but it seems that it is only detecting overlooked bugs in existing code. Which is good news! But in the spirit of not breaking existing code, putting it behind an edition is reasonable.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
But taking a slice of a stack array is legitimate
Which you can always do in @system or @trusted code. You said before that @safe is not just a warning / linting system, it's supposed to be robust. Are we watering it down now, intentionally leaving holes in it?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We want to move to @safe by default. This PR increases safety by default, and the only code it breaks is code that is broken already. It does not carry with it the difficulties that dip1000 has. dip1000 makes @safe safer, but has turned out to be too much of a burden for programmers. I aim to make it safe without those difficulties. This is a good step in that direction.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also don't overlook what Nick said:
BTW DIP1000 is currently enabled for the 2024 edition, so it catches escaping slices anyway.
If I currently revert this PR's code changes, this PR's test cases still pass.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this PR breaks this code for example:
Yes, that is correct. This is why it is only enabled with the 2024 edition. While the code sample is not wrong, it is bad form. I expect more of this to happen with "safe by default". (The false error can be corrected using DFA, but we're not there yet.)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If I currently revert this PR's code changes, this PR's test cases still pass.
If you have dip1000 turned on, yes.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If you have dip1000 turned on, yes.
Or you are using >= 2024 edition:
https://github.com/dlang/dmd/blob/master/compiler/src/dmd/dscope.d#L606
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Your justification for this incomplete fix instead of the simple, obvious, and robust fix was that this way, "the only code it breaks is code that is broken already" and it doesn't add "the noted difficulties of dip1000".
We've now established that, in fact, it does break valid code with the exact same confusing "Error: returning scope variable" message. This is the worst of both worlds: you don't get the safety of DIP1000, but you do get the notorious DIP1000 error messages about scope variables which the programmer didn't actually mark as scope.
I don't approve of a PR which is justified by false statements.
7ebdca1 to
527b4fd
Compare
|
@dkorpel I added the edition check, thanks! |
ca99e47 to
06f66c1
Compare
|
This is ready to go. |
06f66c1 to
658f1bb
Compare
|
@dkorpel spec pr dlang/dlang.org#4356 |
|
@dkorpel spec PR has been merged |
|
That doesn't change anything about #22207 (comment) though |
No description provided.