[Issue 18554] New: `tupleof` ignoring `private` shouldn't be accepted in @safe code

d-bugmail at puremagic.com d-bugmail at puremagic.com
Sun Mar 4 20:51:32 UTC 2018


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

          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 at puremagic.com
          Reporter: ag0aep6g at 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 */
}
----

--


More information about the Digitalmars-d-bugs mailing list