[Issue 17448] New: Move semantics cause memory corruption and cryptic bugs

via Digitalmars-d-bugs digitalmars-d-bugs at puremagic.com
Sun May 28 04:28:46 PDT 2017


https://issues.dlang.org/show_bug.cgi?id=17448

          Issue ID: 17448
           Summary: Move semantics cause memory corruption and cryptic
                    bugs
           Product: D
           Version: D2
          Hardware: x86_64
                OS: Linux
            Status: NEW
          Severity: critical
          Priority: P1
         Component: dmd
          Assignee: nobody at puremagic.com
          Reporter: tomer at weka.io

Using DMD64 D Compiler v2.074.0 on Linux (ubuntu 14.04)

D's move-semantics are really bug-prone and have caused a bug we were chasing
for roughly a week. `@disable this(this)` won't help, and it can be reproduced
in totally safe code.

Basically the ctor of a struct registers a timer to be called within XX
seconds, and this timer is a delegate to a member of this struct. This simple
example demonstrates it:

void delegate() timeoutCallback;

struct CallContext {
    ulong[10] data;
    @disable this(this);
    @disable void opAssign(ref CallContext);

    this(int seconds) {
        // this would be reactor.callIn(seconds, &handleTimeout);
        timeoutCallback = &handleTimeout;
    }
    void handleTimeout() {}
}

auto f() {
    return CallContext(18);
}

void main() {
    auto x = f();
    writeln(&x, " vs ", timeoutCallback.ptr);    // game over: 7FFC3C072640 vs
7FFC3C0725B0
}

With this, it's very easy to produce memory corruption in @safe code:

@safe:

void delegate() timeoutCallback;

struct CallContext {
    ulong[10] data;

    @disable this(this);
    @disable void opAssign(ref CallContext);

    this(int seconds) {
        timeoutCallback = &handleTimeout;
    }
    void handleTimeout() {
        data[0] = 12345;
    }
}

auto f() {auto x = g();}
auto g() {return CallContext(17);}

void h() {
    ulong[20] tmp;
    timeoutCallback();
    foreach(i, n; tmp) {
        writeln(i, "=", n);
    }
}

void main() {
    f();
    h();
}

Which produces

0=0
1=0
2=0
3=0
4=12345
5=0
6=0
7=0
8=0
9=0
10=0
11=0
12=0
13=0
14=0
15=0
16=0
17=0
18=0
19=0

Note that I'm not holding any self-reference, only registering a timer.
Obviously, since the object may be moved several times, it means I can never be
sure the object "won't move anymore" and register this timer...

--


More information about the Digitalmars-d-bugs mailing list