www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.bugs - [Issue 18554] New: `tupleof` ignoring `private` shouldn't be


          Issue ID: 18554
           Summary: `tupleof` ignoring `private` shouldn't be accepted in
                     safe code
           Product: D
           Version: D2
          Hardware: All
                OS: All
            Status: NEW
          Keywords: safe
          Severity: normal
          Priority: P1
         Component: dmd
          Assignee: nobody puremagic.com
          Reporter: ag0aep6g gmail.com

Lifted from the forum:
https://forum.dlang.org/post/p7cmbs$2h1q$1 digitalmars.com

There is an assumption that you can use visibility attributes like `private` to
protect your stuff from outside meddling, and that you can rely on this to
ensure safety (à la  safe).

For example, std.stdio.File assumes this. It `malloc`s its internal data and
keeps a reference count. It does an ` trusted` call to `free` when the count
hits 0. The data is stored in a `private` field, and `File` assumes that it
can't be messed with from the outside, at least not in an ` safe` manner.

As far as I'm aware, that's exactly how ` safe` reference counting is supposed
to be done.

But `tupleof` ignores `private` even in ` safe` code. So it can be used to
violate the assumption, which leads to memory corruption.

The `File` example in code (<https://run.dlang.io/is/1QSsUk>):

void main()  safe
    import std.stdio: File, writeln;
    auto hosts = File("/etc/hosts");
        auto hosts_copy = hosts;
        hosts_copy.tupleof[0].refs = 1; /* uh-oh */
    auto self = File(__FILE__);
    writeln(hosts.rawRead(new char[1000]));
        /* Reads from __FILE__ instead of /etc/hosts. */

And the issue reduced to its core (<https://run.dlang.io/is/reMMQt>):

--- foo.d
struct S
    private int* p;
    this(int x, int y)  safe { p = &[x, y][0]; }
    int get2nd()  trusted { return p is null ? 0 : p[1]; }
        /* Assuming that p is only ever set by the constructor.
        So it's either null or there must be two elements. */

--- bar.d
import foo;
import std.stdio;
void main()  safe
    auto s = S(1, 2);
    s.tupleof[0] = &[10][0]; /* Should not be allowed. */
    writeln(s.get2nd()); /* garbage */

Mar 04 2018