[Issue 14934] New: GC interface doesn't allow safe extension of a memory range

via Digitalmars-d-bugs digitalmars-d-bugs at puremagic.com
Wed Aug 19 00:38:55 PDT 2015


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

          Issue ID: 14934
           Summary: GC interface doesn't allow safe extension of a memory
                    range
           Product: D
           Version: D2
          Hardware: All
                OS: All
            Status: NEW
          Severity: normal
          Priority: P1
         Component: druntime
          Assignee: nobody at puremagic.com
          Reporter: code at benjamin-thaut.de

When extending a existing memory block with realloc which may contain pointers
into the D GC heap the first implementation that comes to mind is:

void* reallocImpl(void* oldPtr, size_t newSize)
{
  void* newPtr = realloc(oldPtr, newSize);
  //GC Point 1
  GC.addRange(newPtr);
  //GC Point 2
  GC.removeRange(oldPtr);
  return newPtr;
}

This implementation has many issues however. 
1) If realloc allocated a new block of memory and freed the old one, the
following issue arises. If the GC triggres at either GC Point 1 or GC Point 2
(from a different thread), the GC will read the old memory block which is
freed. The c-spec states that you should not read this memory after calling
realloc.

2) If realloc did not allocate a new block but extended the existing one, the
GC.addRange call will not do anything because the underling Treap container
ignores duplicates. The remove range call will then remove the old memory block
from the list and as a result the GC will loose any knowdelge about this memory
block.

So a better implementation would look like this.

void* reallocImpl(void* oldPtr, size_t newSize)
{
  GC.disable();
  GC.removeRange(oldPtr);
  // GC Point 1
  void* newPtr = realloc(oldPtr, newSize);
  // GC Point 2
  GC.addRange(newPtr);
  GC.enable();
  return newPtr;
}

This would work if GC.disable would actually guarantee that the GC never runs.
But as the GC might still run in out of memory situations it can happen that is
triggered in either GC Point 1 or GC Point 2. If this happens the GC will not
scan either the old or the new memory block.

I suggest that we add a function to the GC interface that allows safe updating
of a memory range

void* updateRange(void* ptr, scope void* delegate() updateFunc);

updateFunc would be called within the GC lock, preventing any of the issues
described above.

void* reallocImpl(void* oldPtr, size_t newSize)
{
  return GC.updateRange(oldPtr, (){ return realloc(oldPtr, newSize); });
}

--


More information about the Digitalmars-d-bugs mailing list