The GC, destructors, exceptions and memory corruption
Vladimir Panteleev
vladimir at thecybershadow.net
Thu May 12 21:53:43 PDT 2011
Hi,
A while ago, I've tracked down the cause of an insidious memory corruption
problem in one of my D programs. The problem was caused by an inadvertent
allocation in a destructor (called by the GC). The current GC
implementation is completely unprepared to handle such a situation - an
allocation during a GC run will break the GC's invariants, and will
ultimately result in memory corruption. I've filed this problem as issue
5653.
I've created a simple test case which illustrates the problem:
//////////////////////////////////////////////////////////////////////////////
const message = "Hello, world!";
char[] s = null;
class C
{
~this()
{
s = message.dup;
}
}
version(D_Version2)
import core.memory;
else
import std.gc;
void main()
{
C c;
c = new C();
c = new C(); // clobber any references to first instance
version(D_Version2)
GC.collect();
else
fullCollect();
assert(s !is null, "Destructor wasn't called");
assert(s == message, "Memory was corrupted");
}
//////////////////////////////////////////////////////////////////////////////
The exact reason the above program corrupts memory is that .dup will
allocate memory by taking an item from a free list. However, after the
destructor returns, the GC continues on to rebuild the free list with the
information it had before the .dup allocation. The first machine word of
the allocated region will be overwritten with a pointer to the next item
in the free list.
I wrote a patch to the D1 GC to forbid allocations from destructors, and
was considering to port it to D2 and wrap it in a pull request, but
realized that my patch breaks the GC in case a destructor throws. However,
looking at the GC code it doesn't look like the GC is prepared to handle
that situation either... while I haven't noticed any ways in which it
could lead to memory corruption, if the program would catch exceptions
propagated through a GC run, it could lead to persistent memory leaks
(inconsistency between flags and freelists) and destructors of other
objects being called several times (due to free lists not being rebuilt).
Thus, my question is: what's the expected behavior of D programs when a
destructor throws?
--
Best regards,
Vladimir mailto:vladimir at thecybershadow.net
More information about the Digitalmars-d
mailing list