Bug 17318 – Delegates allow escaping reference to stack variable

Status
RESOLVED
Resolution
DUPLICATE
Severity
normal
Priority
P1
Component
dmd
Product
D
Version
D2
Platform
x86_64
OS
Linux
Creation time
2017-04-10T23:40:32Z
Last change time
2018-03-11T00:32:13Z
Keywords
accepts-invalid, safe
Assigned to
No Owner
Creator
hsteoh

Comments

Comment #0 by hsteoh — 2017-04-10T23:40:32Z
Code: ------ import std.stdio; struct S { int x; void delegate() dg; this(int dummy) { dg = () { writefln("&x = %#x", &x); x++; }; } } S makeS() { return S(1); } void main() { auto s = makeS(); writefln("&s.x = %#x", &s.x); s.dg(); } ------ Output: ------ &s.x = 0x7ffdcc81bfe8 &x = 0x7ffdcc81bfb0 ------ The problem here is that dg closes over the member variable x, but the struct resides in stack space. When makeS() returns, the struct is copied somewhere else but the delegate still points to the old address of x. The increment therefore can potentially corrupt the stack. Compiling with -dip1000 does not catch this error. (Note that for some strange reason, I had to comment out the writefln's when compiling with -dip1000; it seems to break some template instantiation code in the compiler and causes the program to fail to link.)
Comment #1 by chilli — 2018-01-20T15:49:16Z
DMD64 D Compiler v2.078.0 and LDC - the LLVM D compiler (1.7.0): based on DMD v2.077.1 and LLVM 5.0.1: I can't reproduce the issue reported; for me, Your code prints: &s.x = 0x7ffd86c2a118 &x = 0x7ffd86c2a118 Equal addresses, independent from with/without -dip1000, didn't need to comment out anything.
Comment #2 by hsteoh — 2018-01-21T00:57:54Z
It could be because the compiler now is smarter about constructing return values in-place rather than moving the struct afterwards. Nevertheless, the actual problem still persists: the delegate is closing over a local variable that resides on the stack. This should have been prevented by -dip1000, but it doesn't. Here's slightly more elaborate example that exposes the bug on latest dmd git master: --------- import std.stdio; struct S { int x; void delegate() dg; this(int dummy) { dg = () { writefln("&x = %#x", &x); x++; }; } } auto makeS() { auto s = S(1); return s.dg; // <-- N.B. escaping reference to s } void smoke(void delegate() dg) { int z = 789; writeln(z); dg(); writeln(z); } void main() { auto dg = makeS(); smoke(dg); } ---------
Comment #3 by hsteoh — 2018-01-21T00:59:26Z
P.S. On my computer, the value of z changes after the call to dg(), which is wrong because z is a local variable in smoke() that the delegate should not be able to access.
Comment #4 by bugzilla — 2018-03-11T00:32:13Z
*** This issue has been marked as a duplicate of issue 18576 ***