[Issue 9614] Uninitialized holes in function stack frames confuses GC

d-bugmail at puremagic.com d-bugmail at puremagic.com
Thu Oct 5 11:55:11 UTC 2017


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

Eugene Wissner <belka at caraus.de> changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
                 CC|                            |belka at caraus.de

--- Comment #2 from Eugene Wissner <belka at caraus.de> ---
This problem affects dmd, with or without -m32, and ldc aswell.

When referencing memory via a variable on the stack, by the time the garbage
collector runs, due to alignment and =voids the stack reference to the variable
has not necessarily been overwritten. As a result, memory may be kept alive by
garbage pointers.

Even calling the GC manually high up the stack does not necessarily solve this,
as by the time the GC determines the stack pointer, it's already several
stackframes down from usercode.

This is especially problematic if the pointer is to a heavily-crossreferenced
data structure, such as an XML tree, where a single pointer anywhere into the
tree may keep the whole thing alive.

The correct solution would be to find a way to make stack garbage collection
type-aware; however, the mechanics of this heavily depend on the compiler
backend.

A compiler-level workaround may be to add a flag to zero any gaps in the stack
and initialize void variables to 0.

Alternatively, a user-level workaround may be to periodically wipe the stack
somewhere early in the calltree.


import core.memory, std.stdio, core.thread;

void test() {
  // Allocate some memory and store it on a variable on the stack.
  auto array = new ubyte[128*1024*1024];
  // The variable's lifetime ends here.
}

void purify() {
  ubyte[0x1000] filler = void;
  import core.stdc.string: memset;
  // To ensure the compiler doesn't optimize out the unused variable, zero it
manually.
  memset(filler.ptr, 0, 0x1000);
}

void main() {
  writeln("Allocate 100MB");
  auto before = GC.stats.usedSize;
  test();
  // purify();
  GC.collect();
  auto after = GC.stats.usedSize;
  assert(after < before + 100*1024*1024, "GC failed to free the allocation!");
  writeln("GC successfully freed allocated memory.");
}

--


More information about the Digitalmars-d-bugs mailing list