Here is an interesting case related to an undocumented feature of GC.realloc(). Although I think there is a bug, I don't want to create a bug report yet because the feature is not even documented. :)

First, here is its most recent definition from druntime/src/gc/gc.d:

void *realloc(void *p, size_t size, uint bits = 0, size_t *alloc_size = null, const TypeInfo ti = null) nothrow
    {
        size_t localAllocSize = void;
        auto oldp = p;
        if(alloc_size is null) alloc_size = &localAllocSize;

// Since a finalizer could launch a new thread, we always need to lock // when collecting. The safest way to do this is to simply always lock
        // when allocating.
        {
            gcLock.lock();
            p = reallocNoSync(p, size, bits, *alloc_size, ti);
            gcLock.unlock();
        }

        if (p !is oldp && !(bits & BlkAttr.NO_SCAN))
        {
            memset(p + size, 0, *alloc_size - size);
        }

        return p;
    }

As can be seen by that memset, it conditionally clears the newly extended part of the memory area. (This is a bonus feature of Phobos over C's standard library as the documentation of C's realloc() says "newly allocated memory will be uninitialized".) However, memset() above starts from 'p + size' where 'size' is the newly requested size. As a result, the area between the old size and the new size are left uninitialized.

The question is especially interesting and easier to demonstrate when 'p' is null because in that case although GC.realloc() tries to work like GC.calloc(), it actually ends up working like GC.malloc().

The output of the following program demonstrates that the initial part of the allocated memory is uninitialized. (I've gotten paranoid and removed the output of running it on my computer; there could be credit card numbers in there. :p)

import std.stdio;
import core.memory;

void main()
{
    const length = 20;
    auto p = GC.realloc(null, length);
    writefln("%2s: %(%02x %)", length, p[0 .. length]);
}

Ali

Reply via email to