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

          Issue ID: 15790
           Summary: The GC frees managed members made w/ allocator.make,
                    causing memory corruption
           Product: D
           Version: D2
          Hardware: All
                OS: All
            Status: NEW
          Severity: normal
          Priority: P1
         Component: phobos
          Assignee: nob...@puremagic.com
          Reporter: b2.t...@gmx.com

The managed members of an aggregate instanciated with
std.experimental.allocator can be freed during a GC pass, and even if the
allocator used is not GCAllocator.

The following program illustrates the issue:

import core.memory;
import std.experimental.allocator.mallocator;
import std.experimental.allocator.common;

enum fill = "azertyuiopqsdfghjklm";

auto make(T, Allocator, A...)(auto ref Allocator alloc, auto ref A args)
{
    import std.algorithm : max;
    import std.conv : emplace;
    auto m = alloc.allocate(max(stateSize!T, 1));

    version(none) GC.addRange(m.ptr, m.length); // activate this to fix the
error

    if (!m.ptr) return null;
    scope(failure) alloc.deallocate(m);
    static if (is(T == class)) return emplace!T(m, args);
    else return emplace(cast(T*) m.ptr, args);
}

struct Node
{
    this(string c){content = c;}
    string content;
    Node*[] nodes;
}

void main()
{
    Node* root = make!Node(Mallocator.instance, fill);
    foreach(immutable i; 0 .. 10000)
    {
        root.nodes ~= make!Node(Mallocator.instance, fill);
        foreach(immutable j; 0 .. 100)
            root.nodes[i].nodes ~= make!Node(Mallocator.instance, fill);
    }
    assert(root.content == fill);
    foreach(immutable i; 0 .. root.nodes.length)
    {
        assert(root.nodes[i].content == fill);
        foreach(immutable j; 0 .. root.nodes[i].nodes.length)
            assert(root.nodes[i].nodes[j].content == fill);
    }
}
Because "content" is a managed type, it's corrupted during a GC pass.
While the tree structure is OK, checking the content reveals the corruption
(.ptr or .length or both points to invalid mem location). When the LOC that
declares the aggregate to the GC is activated, the error disapears.

Either make() documentation should explicitly warn about this or a template
parameter could indicate if the content of the aggregate has to be declared to
the GC.

--

Reply via email to