Rss Feed
Tweeter button
Facebook button
Technorati button
Reddit button
Myspace button
Linkedin button
Webonews button
Delicious button
Digg button
Flickr button
Stumbleupon button
Newsvine button

Category: Development

Unusual memory bit patterns

Software development is an intellectual challenge. Sometimes the process is interrupted by software failures and/or crashes. A lot of the time the reason for the failure is self evident and easily fixable. However the reason for some crashes is less obvious and often the only clue is an unusual bit pattern that is typically present each time the crash happens.

This article will describe some of the common bit patterns that have been used in Windows C/C++ software. After describing the bit patterns we’ll explain various simple rules you can follow in your software development that will make encountering these problems much less likely.

All of these bit patterns apply for the Microsoft C/C++ compiler that ships with Visual Studio (6.0 through 2010) and any compatible compilers (such as the Intel compiler). These bit patterns are found in debug builds. Release builds do not use special bit patterns.

These bit patterns evolve and change as the C runtime internals change from managing memory themselves to handing that management over to the Win32 heap family functions HeapAlloc(), etc.

Some of the allocators appear to know if they are running with a debugger present and change their behaviour so that with a debugger present you will see the bit patterns, and without the debugger present you will not see the bit patterns. HeapAlloc(), LocalAlloc(), GlobalAlloc() and CoTaskMemAlloc() both exhibit this behaviour.

0xcccccccc

The 0xcccccccc bit pattern is used to initialise memory in data that is on the stack.

void uninitFunc_Var()
{
	int	r;

	// here, r has the value 0xcccccccc

	…
}

Also, consider this C++ class.

class testClass
{
public:
	testClass();

	DWORD getValue1();

	DWORD getValue2();

private:
	DWORD	value1;
	DWORD	value2;
};

testClass::testClass()
{
	value1 = 0x12345678;

	// whoops!, forgot to initialise “value2”
}

DWORD testClass::getValue1()
{
	return value1;
}

DWORD testClass::getValue2()
{
	return value2;
}

When an object of type testClass is created on the stack, its data member "value2" is not initialised. However the debug C runtime initialised the stack contents to 0xcccccccc when the stack frame was created. Thus if the object is used, its data member "value2" will have a value 0xcccccccc, and "value1" will have a value of 0x12345678.

void uninitFunc_Object()
{
	testClass	tc;

	// here, tc.value1 has the value 0x12345678
	// here, tc.value2 has the value 0xcccccccc	because it was erroneously not initialised in the constructor

	…
}

If you are seeing the 0xcccccccc bit pattern it means that you are reading memory that is on the current thread stack that has not been initialised.

Why is the pattern 0xcccccccc used? This pattern is four bytes of 0xcc. The Intel i386 opcode 0xcc is breakpoint. Filling the stack with breakpoint instructions for uninitialised data is an interesting choice. Perhaps if a stack corruption occurs and data on the stack starts getting executed there is a slightly greater chance you’ll hit a breakpoint? I don’t know if this was the compiler designer’s intention or just that they chose to use a similar value to the dynamic memory uninitialised value of 0xcd (which we’ll describe later in this article).

0xbaadf00d

The 0xbaadf00d bit pattern is the bit pattern for memory allocated with HeapAlloc(), LocalAlloc(LMEM_FIXED), GlobalAlloc(GMEM_FIXED).

If you are seeing the 0xbaadf00d bit pattern it means that you are reading memory that has been allocated by HeapAlloc() (or reallocated by HeapReAlloc()) and which has not been initialised by the caller of HeapAlloc (or HeapReAlloc, LocalAlloc, GlobalAlloc).

0xdeadbeef

The 0xdeadbeef bit pattern is the bit pattern for memory deallocated using HeapFree(), LocalFree(), GlobalFree().

If you are seeing the 0xdeadbeef bit pattern it means that you are reading memory that has been deallocated by HeapFree(), LocalFree() or GlobalFree().

0xabababab

The 0xabababab bit pattern is the bit pattern for the guard block after memory allocated using HeapAlloc(), LocalAlloc(LMEM_FIXED), GlobalAlloc(GMEM_FIXED) or CoTaskMemAlloc().

If you are seeing the 0xabababab bit pattern it means that you are reading memory after a memory block that has been allocated by HeapAlloc(), LocalAlloc(LMEM_FIXED), GlobalAlloc(GMEM_FIXED) or CoTaskMemAlloc().

0xbdbdbdbd

The 0xbdbdbdbd bit pattern is the guard pattern around memory allocations allocated with the "aligned" allocators.

Memory allocated with malloc(), realloc(), new and new [] are provided with a guard block before and after the memory allocation. When this happens with an aligned memory allocator, the bit pattern used in the guard block is 0xbdbdbdbd.

If you are seeing the 0xbdbdbdbd bit pattern it means that you are reading memory before the start of a memory block created by an aligned allocation.

0xfdfdfdfd

The 0xfdfdfdfd bit pattern is the guard pattern around memory allocations allocated with the "non-aligned" (default) allocators.

Memory allocated with malloc(), realloc(), new and new [] are provided with a guard block before and after the memory allocation. When this happens with an non-aligned (default) memory allocator, the bit pattern used in the guard block is 0xfdfdfdfd.

If you are seeing the 0xfdfdfdfd bit pattern it means that you are reading memory either before the start of a memory block or past the end of a memory block. In either case the memory has been allocated by malloc(), realloc() or new.

0xcdcdcdcd

The 0xcdcdcdcd bit pattern indicates that this memory has been initialised by the memory allocator (malloc() or new) but has not been initialised by your software (object constructor or local code).

If you are seeing the 0xcdcdcdcd bit pattern it means that you are reading memory that has been allocated by malloc(), realloc() or new, but which has not been initialised.

0xdddddddd

The 0xdddddddd bit pattern indicates that this memory is part of a deallocated memory allocation (free() or delete).

If you are seeing the 0xdddddddd bit pattern it means that you are reading memory that has been deallocated by free() or delete.

0xfeeefeee

The 0xfeeefeee bit pattern indicates that this memory is part of a deallocated memory allocation (free() or delete).

If you are seeing the 0xfeeefeee bit pattern it means that you are reading memory that has been deallocated by free() or delete.

What can you do to prevent the cause of these problems?

Depending on the nature of the problem, it may be trivial to identify why you have an uninitialised variable or why your pointer is pointing to deallocated memory. The rest of the time you can rely on a few helpful rules and the judicious use of some software tools

Crash

When you have a crash, try to identify the cause of the crash.

  • If you are in a debugger, the debugger may be able to show you variable names, thus you will be able to identify which variable is in error.
  • If you are in a debugger you can view the register window to see if a register has one of these special bit patterns.
  • If you have an exception report, you can view the crash address, the exception address and any registers to see if any of these special bit patterns are present.

Registers

At the start of a C++ method, the "this" pointer is stored in the ECX register (RCX on 64 bit/x64). If the ECX register contains one of these bit patterns you have a good indicator that a dangling pointer to a deleted object or an uninitialised pointer is being used. Note that depending on what the compiler does the ECX register may remain valid during the method or may take on other values and thus be unreliable as to whether it still represents the quot;this" pointer.

When a method or function returns, the return value is in the EAX register (RAX for 64 bit/x64).

Simple data member rules to follow

  • Always initialise all data members in all your object constructors.
  • Always initialise all data that is used that is not data members of class definitions.
  • Always use the correct form of delete, even if you are working with intrinsic types. It is not unknown to change the type of a data member as software evolves. If you always use the correct form of delete, then a switch from (for example) "int" to a class type will not result in objects of the class type failing to be deleted.

Simple C/C++ allocator rules to follow

  • If you allocate using new, deallocate with delete.
  • If you allocate using new [], deallocate with delete [].
  • If you allocate using malloc() or realloc(), deallocate with free().

Simple pointer rules to follow

  • Always set pointers to dynamically allocated data to NULL before using them (unless you are assigning them on first use).
  • Always check pointers are non-NULL before you use them.
  • Always set pointers to NULL after dellocating the memory pointed to by the pointer.
    	delete [] accounts;
    	accounts = NULL;
     
    Do this in object destructors and anywhere else you deallocate memory. The reason you do this in object destructors is because in release code, the memory deallocator will not overwrite the contents of the deleted object. Thus if any erroneous code is still pointing to the deleted object, it will find a NULL pointer to accounts rather than a pointer to a deleted accounts object.
  • In object destructors, even if you are not deleting any objects, but you have pointers to objects, set these to NULL as well. The reason is the same reason as above – defensive programming, minimise the likelihood things can fail.
  • If possible, try to make one location in your software responsible for the management of a particular type of object. For example you may create a manager class that is responsible for creating, managing and destroying particular objects. If you then get a failure with a related object type, you can focus your attentions on the manager class, perhaps adding additional tracing code or analysing with a software tool.

Software libraries in DLLs – rules to follow

If your software is structured so that some class implementations are in certain DLLs and other DLLs rely on those class implementations (either by usage or by derivation/inheritance) then you need to consider the following issues:

  • Size

    If you change the size of a struct, union or class object then you will need to rebuild all other DLLs that use that struct, union or class. In theory the Developer Studio build system should keep everything up to date. But Developer Studio can only work with the information you give it. If you have forgotten to add appropriate header files to a project the project will not consider changes to those header files. Typically we have found the simplest and easier solution is a full rebuild of any affected DLLs after a change in size of a class object. Reasons for struct, union or class to change size:

    • Change of a method from normal to virtual or virtual to normal.
    • Addition or removal of a virtual method.
    • Addition or removal of a data member.
    • Change in type of an object that is a data member of the class (for example from int to double or from weeble to wobble).
    • Change in size of an object that is a data member of the class (for example data member of type testObject changes size).
    • Change of #pragma byte packing for some or all of the class definition.
    • Addition or removal of base classes which the class derives from.
    • Conditional compilation resulting in different data members, same data members but different ordering or different definitions for data members.
    • Macro definitions resulting in different types/sizes for data members that are not obvious. This is an obscure form of the conditional compilation problem.

    A good indicator that you have forgotten to do the above, is looking at the class object in the debugger and noticing that the fields all appear OK in one DLL, but when looked at from a function in a different DLL, the object fields appear to have different data. This is a sure sign that one or more DLLs have not been compiled with the same object definition.

  • Dynamic memory

    Passing data back from DLLs can be fraught with problems.

    The classic problem is A.dll calls B.dll to get some data. The data is dynamically allocated, populated with the result and passed back to the caller. The caller uses the data then deallocates the data. The program crashes. Why? The typical reason is that the deallocator used to deallocate the memory is not the correct deallocator to match the allocator of the memory. “But! But I’m using the C runtime all the time” exclaims the hapless and confused developer. “What have I done wrong?”.

    Typical scenarios for this are as follows:

    #AllocatorDeallocatorResult
    1Dynamically linked CRT (dynB.dll)Dynamically linked CRT (dynA.dll)OK
    2Dynamically linked CRT (dynB.dll)Statically linked CRT (statA.dll)Crash
    3Statically linked CRT (statB.dll)Dynamically linked CRT (dynA.dll)Crash
    4Statically linked CRT (statB.dll)Statically linked CRT (statA.dll)Crash
    5Statically linked CRT (statB.dll)Statically linked CRT (statB.dll)OK

    Lets take each scenario in turn.

    #1. Both dynA.dll and dynB.dll are linked to the dynamic C Runtime. This means they are both using the same allocator and deallocator functions (malloc/free, new/delete) in the same C runtime DLL. That dll is used by both dynA.dll and dynB.dll. Because the same runtime is used by both dynA.dll and dynB.dll the correct deallocator is used to deallocate the memory allocated by dynB.dll. This scenario works correctly. dynA_dll_dynB_dll

    #2. dynB.dll is linked to the dynamic C Runtime. It’s caller statA.dll is linked to the static C Runtime. The allocated memory comes from the dynamic CRT. The deallocation function is in the static CRT which uses a different heap to the dynamic CRT. The memory deallocation call fails. Most likely with a crash. statA_dll_dynB_dll

    #3. statB.dll is linked to the static C Runtime. It’s caller dynA.dll is linked to the dynamic C Runtime. The allocated memory comes from the static CRT. The deallocation function is in the dynamic CRT which uses a different heap to the dynamic CRT. The memory deallocation call fails. Most likely with a crash. dynA_dll_statB_dll

    #4. statB.dll is linked to the static C Runtime. It’s caller statA.dll is linked to the static C Runtime. The allocated memory comes from a static CRT linked to statB.dll. The deallocation function is in the static CRT linked to statA.dll which uses a different heap to the other static CRT. The memory deallocation call fails. Most likely with a crash. statA_dll_statB_dll_crash

    #5. statB.dll is linked to the static C Runtime. statA.dll can be linked to the static C Runtime or the dynamic C Runtime. Memory is allocated in statB.dll, passed back to the caller, used and then passed the memory back to a function in statB.dll that calls the deallocator. That is, statB.dll has to provide an explicit cleanup function to deallocate the memory it passed back to the caller. All allocated memory is deallocated in the same DLL that it was allocated in. It doesn’t matter how the calling DLL is linked to the C Runtime. This scenario works correctly. statA_dll_statB_dll_OK

  • ANSI / Unicode

    You need to ensure that APIs between DLLs use the the string width (char/byte for ANSI, two byte (wchar_t) for wide character) that each DLL expects. If you have C++ functions you’re golden as the name mangling will ensure the correct values are passed. But if you are exposing C style APIs and calling them via GetProcAddress you had better make sure your function prototypes match the DLL otherwise you could end up passing good data to the wrong function (which will then perceive that data as bad data).

I’ve tried all the above, what other options are there?

If all else fails, then you will probably need to turn to a software tool to analyse your memory usage and identify the cause of the deleted memory that the pointer is pointing to (has the memory been deleted too early? Or is the pointer being used in error after the memory was deleted?).

In that case you may want to consider a software tool like C++ Memory Validator.

Would you like to know when we post a new article?

Get expert advice on software engineering topics. No spam.

Why Process Injection Fails

If you are reading this web page it is most likely because you have just tried to inject into a running process and the injection failed. You’ve probably just viewed an information dialog similar to the one shown below.

ProcesInjectFailed

For the purposes of this article we’ll talk about C++ Memory Validator, but the points all apply to any of our software tools that support process injection (C++ Bug Validator, C++ Coverage Validator,┬áC++ Memory Validator, C++ Performance Validator, C++ Thread Validator)

When can injection fail?

There are three different places in our tools where injection can happen.

  1. Injecting into a running process.
  2. Waiting for a process to start and then attaching to it automatically when it starts.
  3. Launching a process when the launch method is set to any value other than CreateProcess. CreateProcess is the recommended method of launching a process. The other methods preceeded CreateProcess and all use process injection coupled with varying delays or varying process security settings to acheive their aim. Because launching with CreateProcess is so reliable (close to 100%) you are unlikely to ever use any of these other methods (which are less reliable, less than 95%).

Causes of injection failure

  • A missing DLL in your application.
    This can only be a problem if you are launching an application (item 3 above). The application will fail to launch properly if all DLL dependencies are not met. The application will start then very rapidly shutdown due to a missing DLL dependency. When this happens it is not possible to inject into the application as it doesn’t run for long enough.
  • A missing DLL in the software tool (e.g. C++ Memory Validator) you are using to inspect your application.
    This should never happen. This bug will only happen if a mistake has been made at Software Verification when creating the software installer. We list this here for completeness.
  • Multiple injection attempts.
    If you have already successfully injected into this running process you can’t inject into it again because the injected DLL is already loaded.

    The solution to this problem is to start a new process and inject into that process.

  • The application started and finished before the DLL could be injected.
    If your process only runs for a short amount of time the process may finish executing before process injection can complete. An example would a command line tool that loads, processes data then closes.
  • The application security settings do not allow process handles to be opened.
    If your application is running at a privilege level that means that C++ Memory Validator cannot open the appropriate process handle to perform the injection it will be impossible to make the appropriate actions to perform an DLL injection into your application. This is quite common when working with services, but can apply to any process that is running with particular privileges. This is one of the reasons that C++ Memory Validator runs with admin rights and require User Access Control confirmation upon startup – we do this to get a good baseline set of privileges to work with.
  • The application is a service.
    Services run in a different environment to regular applications. Services are controlled differently. Typically they run on different accounts to regular applications and with different security privileges and different access rights to parts of the system (for example, no disk access except for a particular folder, no shared memory access, etc). As such it is often impossible to gain access to the service to perform a process injection.
  • The service and C++ Memory Validator should both run on the same user account.
    If you are working with a service, then ensuring that both the service and C++ Memory Validator run on the same account can sometimes resolve some problems.
  • Injecting into some processes just does not work.
    Even when all of the above issues are resolved or do not apply some applications just do not want to be injected into. This applies for 32 bit processes injecting into 32 bit processes and 64 bit processes injecting into 64 bit processes.

    That sounds like a bogus claim with no data to back it up.

    When we first started testing our process injection software in 1999 we built a test rig that would test every application on the test computer. A test would be start the process, inject into it, send the process a WM_QUIT and wait for it to quit. We’d monitor if the injection was successful for each test. A test run would take hours and would test several thousand applications. We started testing on Windows NT 4. Then when Windows 2000 was released we tested that. We did the same tests for Windows XP, etc.

    What we found was that the failure rate for injecting on Windows NT was 2%. On Windows 2000 it was 3%. On Windows XP it was 5%. Each new variant of windows was more complex, it typically ran on more complex and more modern hardware. These variables, changes in multi-tasking scheduling and changes in operating system behaviour and timing combined with the hardware seemed to make process injection less reliable with each new version of Windows. But couldn’t the problem be that we’re injecting a really complex DLL (for example, the C++ Memory Validator DLL)? Surely the cause of the failure is the complexity of what the DLL is doing?

    That’s a really good question and that is why we did some tests with an empty DLL. An empty DLL for our experiments was a DLL that consists of nothing more than a DllMain(). See below.

    #include "stdafx.h"
    
    BOOL DllMain(HANDLE hModule,
                 DWORD  dwReason,
                 LPVOID lpReserved)
    {
        return TRUE;
    }
    

    When we tested with a DLL consisting of nothing more than the code above compiled into a DLL we found that even this trivial DLL could not be injected into application that we had previously had failures with.

    Clearly the DLL injection problem we were encountering was real and nothing to do with the code (or DLL dependencies) of the DLL we were trying to inject.

Solutions to being unable to inject into a process.

Given that we know from our testing (detailed above) that if injection doesn’t work for a particular application, it typically won’t work for that application, we strongly advise you to stop spending any more time trying to make process injection work for the particular application that is giving you trouble. We’ve spent years looking at this. It really is not worth your time and trouble.

So what can you do?

  • Launch
    Based on feedback we’ve found that some people were using process injection because they had not realised the power of the command line options for running our software tools from the command line. They were launching the process they wanted to instrument, then our software tool and doing the process injection because their application had already started. In some cases they had launched their test application from a unit testing application.

    If this is your scenario it is worth examining the command line argument for the software tool to see if you can achieve what you need to achieve without process injection.

  • NT Service API
    If you are working with services we recommend using the NT Services API which is described in detail in the Help file for the software tool. The NT Services API also comes with example usage, and example client and example service so that you can see how easy it is to integrate the NT Service API into your service. Using these APIs makes working with services really easy.
  • Linkable API
    Some of our software tools have an additional API known as the linkable API. This API is different to the NT Services API. You may find using this API helpful if you don’t wish to use the NT Services API.

Help files can be found from the Help menu on the software tool and also in our Help file download area..

Problems with Process Injection.

Even when process injection succeeds and our profiling DLL is loaded into the target application there can be problems.

The principle problem is with multi-threaded applications that have already have more than one thread by the time the process injection completes. Because process injection is not supported by the operating system and is achieved through the use of some clever memory mapping and thread execution you cannot guarantee which synchronization primitives are locked at the time of the injection. For many applications this is not an issue and injection succeeds but for some applications locks can be held and these conflict with the locks required to be held to do the job of profiling the DLL during the injection process. When this happens the target process deadlocks. This cannot be forseen. This behaviour is application specific.

If process injection succeeds but then your process deadlocks this means you cannot reliably use process injection for this application. As such, even though we’ve provided the ability to do process injection we recommend launching your process with our software tool or using the NT Services API to ensure your process is instrumented.

Recommended course of action

In summary, if you ever have the opportunity to launch your process with C++ Memory Validator (using the CreateProcess option) you should use that.

If you can’t launch your process you can try process injection (or wait for process). If those do not work for the reasons stated in this article we recommend that you use the appropriate NT Service API for that tool you are using (see the details in the Help file for that tool).

Would you like to know when we post a new article?

Get expert advice on software engineering topics. No spam.

Panorama Theme by Themocracy