Bug 5270 – Using a scope delegate allows memory corruption in safe mode

Status
RESOLVED
Resolution
FIXED
Severity
major
Priority
P2
Component
dmd
Product
D
Version
D2
Platform
All
OS
All
Creation time
2010-11-24T09:04:42Z
Last change time
2018-03-04T12:44:15Z
Keywords
accepts-invalid, pull, safe, spec
Assigned to
No Owner
Creator
Lars T. Kyllingstad

Comments

Comment #0 by bugzilla — 2010-11-24T09:04:42Z
The following program compiles and runs without error. Memory corruption happens in bar() because the context for the delegate stored in globalDg never gets copied to the heap, due to the 'scope' storage class being used in call(). @safe: void delegate() globalDg; void call(scope void delegate() @safe dg) { dg(); // Don't tell anyone, but I'm saving this for later ;) globalDg = dg; } void foo() { int i; void izero() { i = 0; } call(&izero); assert (i == 0); } void bar() { int x = 123; // Simply calling some function cannot possibly // do anything to x... globalDg(); // ...or can it? assert (x == 0); } void main() { foo(); bar(); }
Comment #1 by bugzilla — 2012-01-23T23:48:02Z
This now compiles & runs successfully.
Comment #2 by timon.gehr — 2012-01-24T07:38:16Z
The issue is that "it compiles and runs without error". (the second assertion asserts that there is memory corruption) The compiler has to either: - enforce the 'scope' storage class in @safe mode by flow-analysis. - not perform the scope delegate optimization in @safe mode. Change the second assertion to 'assert (x == 123);' to see the error.
Comment #3 by hsteoh — 2014-07-09T22:21:26Z
*** Issue 13085 has been marked as a duplicate of this issue. ***
Comment #4 by hsteoh — 2014-07-09T22:22:41Z
Another failing case that illustrates the problem: ------ int delegate() globDg; void func(scope int delegate() dg) { globDg = dg; // should be rejected but isn't globDg(); } void sub() { int x; func(() { return ++x; }); } void trashme() { import std.stdio; writeln(globDg()); // prints garbage } void main() { sub(); trashme(); } ------
Comment #5 by hsteoh — 2014-07-09T22:25:26Z
Removing 'scope' from func's parameter fixes the problem, since the compiler will then allocate x on the heap instead of the stack.
Comment #6 by bugzilla — 2016-08-25T07:07:57Z
(In reply to hsteoh from comment #4) > Another failing case that illustrates the problem: > ------ > int delegate() globDg; > > void func(scope int delegate() dg) { > globDg = dg; // should be rejected but isn't > globDg(); > } Annotating func() with @safe results in: test3.d(4): Error: scope variable dg assigned to non-scope globDg with Pull Request: https://github.com/dlang/dmd/pull/5972 This check is limited to @safe code to avoid breaking too much existing code.
Comment #7 by bugzilla — 2016-08-25T07:09:40Z
(In reply to timon.gehr from comment #2) > The issue is that "it compiles and runs without error". (the second > assertion asserts that there is memory corruption) The compiler has to > either: > > - enforce the 'scope' storage class in @safe mode by flow-analysis. > - not perform the scope delegate optimization in @safe mode. > > Change the second assertion to 'assert (x == 123);' to see the error. Pull https://github.com/dlang/dmd/pull/5972 now causes: test3.d(9): Error: scope variable dg assigned to non-scope globalDg
Comment #8 by bugzilla — 2016-08-25T07:10:13Z
This can be closed once 5972 is pulled.
Comment #9 by bugzilla — 2018-03-04T01:33:03Z
(In reply to Walter Bright from comment #8) > This can be closed once 5972 is pulled. It was pulled.
Comment #10 by dfj1esp02 — 2018-03-04T12:44:15Z
(In reply to Walter Bright from comment #6) > This check is limited to @safe code to avoid breaking too much existing code. For the record: escaping scoped data should stay allowed is unsafe code, it's a useful pattern, the code would just respect scoping manually.