Debugging Memory Corruption Errors with Dynamic Stack Checking

This tutorial shows how to use the dynamic stack checking feature of VisualGDB 5.2 to quickly locate code stack overflows before they create hard-to-trace memory corruption errors.

Before you begin, install VisualGDB 5.2 or later.

  1. Start Visual Studio and open the Embedded Project Wizard:01-wizard
  2. We will create a normal embedded project, so proceed with the default settings on the first page:02-prjtype
  3. On the next page select your device. In this tutorial we will use the STM32F4-Discovery board that has the STM32F407VG chip:03-deviceThe dynamic stack checking is not specific to STM32 devices and will work with all modern ARM Cortex devices.
  4. On the next page select the basic “LEDBlink” sample and click “Next”:04-blink
  5. Finally select your debug method. If you select OpenOCD and click “detect”, VisualGDB should be able to detect the rest of the settings automatically:05-debug
  6. Press “Finish” to create the project. Then open VisualGDB Project Properties, go to the Embedded Frameworks page and reference the “Fixed stack and heap” framework. Then enter the stack and heap sizes to fill the device RAM almost completely. Pick a large value for the heap that is larger than the device RAM (we will decrease it later):06-fixedovf
  7. Replace the contents of the main file with the following code:

    It will repeatedly try to allocate all of the available memory using malloc() display the amount allocated and then free it.
  8. Try building the project. The linker will report a memory overflow showing how many extra bytes did not fit:07-linkovf
  9. Reduce the heap size on the Embedded Frameworks page by the value shown in the link error (e.g. 20000-75536=124464) and build the project again:08-build
  10. Now you can run the program and check that the memory is repeatedly allocated without any problems:09-semihostok
  11. Now we will add code that will corrupt the memory by overrunning the stack. Add 3 functions shown below and call func1() from your SysTick_Handler():

    Each of the 3 functions will try to use 2KB of memory, quickly adding up to more than the 4KB stack size selected in VisualGDB Project Properties.
  12. Run the project now. You will see how the output quickly stops and if you press the ‘break all’ button, you will see that the program is stuck at the hard fault handler caused by the free() function:10-handler
  13. If this was a real project and we did not know what is corrupting the stack, it could take several hours to pinpoint it using the regular means. The new Dynamic Stack Analysis introduced in VisualGDB 5.2 makes it much easier by inserting stack bounds checking code in each of your functions. Open VisualGDB Project Properties and go to the Dynamic Analysis page (requires Custom edition or higher). Enable the “Instrument the functions in your program to check for stack overflow” checkbox:11-analyze
  14. Then click “Enable stack usage reporting” and “Add reference automatically” to adjust the necessary project properties and build your project. Most likely you would need to reduce the heap size again to fit the fast semihosting buffer used by the profiler framework:heap2
  15. If you run your project now, VisualGDB will immediately detect the stack overflow and will show a detailed report:12-report
  16. Click “break” and examine the call stack window to see when the overflow happened. In this example it happens when a timer interrupt happens while the malloc() function is running:13-trace
  17. Dynamic checking has a side effect of slowing down your program. If you open the Disassembly view, you will notice that all function calls were replaced with calls to special profiler stubs:14-step
  18. Each stub invokes the SysprogsStackVerifierHook() function that does the actual stack checking:15-hook
  19. You can exclude some of the functions from stack checks via the Dynamic Analysis page of VisualGDB Project Properties. E.g. we can exclude the offending func2() function to see how this affects the analysis:16-nofunc2
  20. If you run your program now, the overflow won’t be detected upon entry to func2(), but will still be caught when memset() is called:17-memsetWhen you are done analyzing your project, you don’t need to rebuild it again. Simply disable the stack checking on the Dynamic Analysis page of VisualGDB Project Properties and VisualGDB will not instrument your functions until you enable this feature again. The profiler framework will be compiled in, but won’t be activated unless you explicitly start profiling or enable analysis again.