Bug 22539 – [dip1000] slicing of returned ref scope static array should not be allowed

Status
RESOLVED
Resolution
FIXED
Severity
normal
Priority
P1
Component
dmd
Product
D
Version
D2
Platform
All
OS
All
Creation time
2021-11-23T19:38:16Z
Last change time
2022-03-10T08:34:11Z
Keywords
pull
Assigned to
No Owner
Creator
Dennis

Comments

Comment #0 by dkorpel — 2021-11-23T19:38:16Z
Because there is no transitive scope, taking the address of a scope variable is not allowed. There are 4 cases: - ref param &pointer: rejected with `checkAddresVar` - ref param slice[]: rejected with `checkAddresVar` - ref return &pointer: too restrictive (issue 22519) - ref return slice[]: too lenient (this issue) This allows you to escape scope pointers: ``` // REQUIRED_ARGS -preview=dip1000 @safe: ref int*[1] identity(ref return scope int*[1] x) { return x; } int* escape() { int stackVar = 0xFF; scope int*[1] x = [&stackVar]; int*[] y = identity(x)[]; return y[0]; } void main() { int* dangling = escape(); } ```
Comment #1 by dlang-bot — 2021-11-26T17:17:00Z
@dkorpel created dlang/dmd pull request #13362 "Fix issue 22539 - slicing of returned ref scope static array should not be allowed" fixing this issue: - Fix issue 22539 - slicing of returned ref scope static array should not be allowed https://github.com/dlang/dmd/pull/13362
Comment #2 by bugzilla — 2021-11-27T19:57:20Z
I suspect the problem is actually this line: scope int*[1] x = [&stackVar]; Since scope is not transitive, allowing this causes the compiler to lose track of stack addresses, and will allow escapes.
Comment #3 by dkorpel — 2021-11-27T20:08:37Z
(In reply to Walter Bright from comment #2) > I suspect the problem is actually this line: > > scope int*[1] x = [&stackVar]; > > Since scope is not transitive, allowing this causes the compiler to lose > track of stack addresses, and will allow escapes. Only if x were a slice, but since it's a static array with dimension 1 it is identical to: scope int* x = &stackVar; So it is correctly permitted.
Comment #4 by bugzilla — 2021-11-27T20:15:07Z
Add @safe to escape() and it will fail to compile.
Comment #5 by bugzilla — 2021-11-27T20:19:19Z
P.S. many times I've chased rabbits only to discover I forgot to add @safe. @safe really should be the default.
Comment #6 by dkorpel — 2021-11-27T20:19:36Z
(In reply to Walter Bright from comment #4) > Add @safe to escape() and it will fail to compile. `escape` is already under `@safe:`. I added `@safe` to escape explicitly to double check, it still compiles on my end. What error do you get?
Comment #7 by dkorpel — 2021-11-27T20:23:14Z
(In reply to Walter Bright from comment #5) > P.S. many times I've chased rabbits only to discover I forgot to add @safe. > @safe really should be the default. I didn't forget @safe, I added @safe: on top (as I usually do).
Comment #8 by bugzilla — 2021-11-27T20:26:43Z
My bad, adding -dip1000 removes the error message. I had neglected to do that.
Comment #9 by dkorpel — 2021-11-27T20:30:24Z
(In reply to Walter Bright from comment #8) > My bad, adding -dip1000 removes the error message. I had neglected to do > that. I guess -dip1000 really should be the default :)
Comment #10 by bugzilla — 2021-11-27T21:29:00Z
Moving on from my previous mistakes: @safe: ref int* identity(ref return scope int* x) { return x; } int* escape() { int stackVar; scope int* x = &stackVar; int* y = identity(x); return y; // error: scope variable `y` may not be returned } Good. Next, @safe: ref int*[1] identity(ref return scope int*[1] x) { return x; } int*[1] escape() { int stackVar; scope int*[1] x = [&stackVar]; int*[1] y = identity(x); return y; // error: scope variable `y` may not be returned } Good. int* escape() { int stackVar; scope int*[1] x = [&stackVar]; int*[1] y = identity(x); return y[0]; // error: scope variable `y` may not be returned } Good. int* escape() { int stackVar; scope int*[1] x = [&stackVar]; int*[1] y = identity(x); int*[] z = y[]; // error: cannot take address of `scope` local `y` return z[0]; } Good. Here, the slicing of `y` is properly detected. But if we put the [] after the identity call: int* escape() { int stackVar; scope int*[1] x = [&stackVar]; int*[] y = identity(x)[]; return y[0]; } No error; bad. The slicing of the return value of identity() is not properly propagating the scope-ness of its argument x. This would be a problem in escape.d.
Comment #11 by dkorpel — 2021-11-27T22:41:19Z
(In reply to Walter Bright from comment #10) > No error; bad. The slicing of the return value of identity() is not properly > propagating the scope-ness of its argument x. This would be a problem in > escape.d. Yes, the scope-ness is lost because there is no transitive scope, so the slicing shouldn't be allowed in @safe code. It's essentially the same as: https://issues.dlang.org/show_bug.cgi?id=20691 Which you fixed with: https://github.com/dlang/dmd/pull/10951 But that fix is limited to variables, not return values, so my pull request extends it by also considering slicing a CallExp: https://github.com/dlang/dmd/pull/13362 I don't understand what your comment is getting at, do you have any objections to this?
Comment #12 by bugzilla — 2021-11-28T02:41:21Z
Objections? Not at the moment, I'm just trying to thoroughly understand the problem.
Comment #13 by bugzilla — 2021-11-29T00:07:54Z
(In reply to Walter Bright from comment #12) > Objections? Not at the moment, I'm just trying to thoroughly understand the > problem. See the PR for my remaining objection. Thanks for your patience with this.
Comment #14 by dlang-bot — 2022-03-10T08:34:11Z
dlang/dmd pull request #13362 "Fix issue 22539 - slicing of returned ref scope static array should not be allowed" was merged into master: - 8f3f72f93a6b6241342c495c569f294c4ea0238f by dkorpel: Fix issue 22539 - slicing of returned ref scope static array should not be allowed https://github.com/dlang/dmd/pull/13362