This is how a debug build behaves, compiled with VS2013. Release build is different and the differences are a bit baffling. I’ll write about that later.
I’ve spent a lot of time hunting an elusive memory corruption bug. At least it looked like memory corruption: release build just crashed on access violation, debug build was throwing a CRT assert on free().
Evidently something was wrong with the heap when free() was called — debug builds perform heap checking after a certain number of heap operations. You can force heap checking at every (de)allocation with _CrtSetDbgFlag(), it’s a good idea in cases like this when it wasn’t immediately obvious why the free() would cause problems. The memory was good, no double-freeing was occurring. Yet commenting that free() out seemed to remove all problems (memory leaks notwithstanding).
I’ve been scratching my head for a bit, tried the Application Verifier, nothing seemed to bring me closer to diagnosing the core issue. Finally I analyzed where this freed memory was coming from. It was allocated in a call imported from a utility DLL. Gears started to move faster in my head when I checked the DLL’s project settings, specifically how the C runtime was linked. It was linked statically.
Why was that causing problems? Well, when the CRT is statically linked to a DLL, that DLL creates its own CRT heap for your malloc()s and free()s. That means if you malloc() something in a DLL with static CRT you can’t free() it from the main program because the free() will operate on a different CRT heap. You can see the virtual memory breakdown of a process with VMMap, an excellent Sysinternals utility.
We can see the additional heaps when the DLL contains statically linked CRT.
What’s the moral of the story?
- Never link the CRT statically unless you really know what you’re doing. It may be tempting to avoid the need of CRT run-time DLLs but it’s almost always not worth it. Why duplicate the CRT code in your executables? Just ship the CRT redistributables with your installer.
- If you really need static CRT in your DLL, free all DLL-allocated memory in the DLL, not outside of it.