Search code examples
visual-c++memory-managementmsvcrt

Controling CRT memory initialization


Occasionally you meet bugs that are reproducible only in release builds and/or only on some machines. A common (but by no means only) reason is uninitialized variables, that are subject to random behaviour. E.g, an uninitialized BOOL can be TRUE most of the time, on most machines, but randomly be initialized as FALSE.

What I wish I would have is a systematic way of flushing out such bugs by modifying the behaviour of the CRT memory initialization. I'm well aware of the MS debug CRT magic numbers - at the very least I'd like to have a trigger to turn 0xCDCDCDCD (the pattern that initializes freshly allocated memory) to zeros. I suspect one would be able to easily smoke out nasty initialization pests this way, even in debug builds.

Am I missing an available CRT hook (API, registry key, whatever) that enables this? Anyone has other ideas to get there?

[Edit:] Seems clarifications are in order.

  1. The usual magic numbers indeed have many advantages, but they do not provide coverage for bool initializations (always true), or bit fields that are tested against individual bit masks, or similar cases. Consistent zero initialization (that I'm able to toggle on and off, of course), would add a layer of testing that can surface bad init behaviour that can be rare otherwise.
  2. I'm aware, of course, of CrtSetAllocHook. The hook thus set does not receive a pointer to the allocated buffer (it is called before such buffer was allocated), so it cannot overwrite it. Overloading global new wouldn't do much good either, as it would overwrite any valid constructor initialization.

[Edit:] @Michael, not sure what you mean by overriding new. Simple code like -

void* new(...)
{
   void* res = ::new(...);   // constructors now called!
   if(SomeExternalConditionApplies())
      OverWriteBufferWithMyPetValues(res);
}

would not work. Pasting and modifying the entire ::new code might work, but seems kinda scary (god only knows what I'd have to #include and link to get it to run).


Solution

  • I'm not following - having uninitialized memory set to something like 0xcdcdcdcd instead of 0 is better for flushing out bugs, because code is more likely get 'in range' arithmetic or specially handle 0. With the wildly invalid value, bugs are more likely to 'fail fast' so they can be fixed instead of hidden.

    The values that the debug build of MSVC uses are specifically designed to help cause failures that can be easily detected:

    • they aren't 0, so NULL pointer checks against uninitialized memory won't hide the bug
    • they aren't valid pointers, so dereferencing uninitialized pointers will cause an access violation
    • they aren't 'usual' integer values, so calculations involving uninitialized data will usually result in wildly incorrect results that will tend to cause noticeable failures (I think be negative when handled as signed data also helps a bit with this, but not as much as simply being unusual numbers).

    Also, they're easy to recognize in data displays in the debugger. Zero doesn't stand out nearly as much.

    All that said, MSVC provides a number of debug hooks/APIs that you might be able to use to do something along the lines of what you want:

    Some additional information in response to your updated question:

    You might be able to use a 3rd party debug allocation library like Dmalloc (http://dmalloc.com/) but I honestly don't know how easy those libraries are to integrate into an MSVC project, especially 'real world' ones.

    Also, note that these obviously will only deal with dynamic allocations (and might not integrate well with MSVC's default new implementation).

    You can use a global override of operator new() to deal with allocations that occur using new in C++ - there's not a problem with overwriting any valid constructor initializations - the new allocation occurs before the constructor performs any initialization (if you think about it for a moment it should be clear why that is).

    Also, you might want to consider moving to Visual Studio 2010 - it will break into the debugger when you use an uninitialized local variable - doing nothing special other than running the Debug build under the debugger. Of course, MSCV has warned about many of these situations for a while, but VS2010 will catch the following in the debugger, which produces no warnings (at least with my current compiler settings):

    int main(  )
    {
        unsigned int x;
        volatile bool a = false;
    
        if (a) {
            x = 0;
        }
    
        printf( "Hello world %u\n", x); // VS2010 will break here because x is uninitialized
    
        return 0;
    }
    

    Even the Express version of VC++ 2010 supports this.