C++ Safe-stack (Stack corruptions)


Example

Stack corruptions are annoying bugs to look at. As the stack is corrupted, the debugger often can't give you a good stack trace of where you are and how you got there.

This is where safe-stack comes into play. Instead of using a single stack for your threads, it will use two: A safe stack and a dangerous stack. The safe stack works exactly like it did before, except that some parts are moved to the dangerous stack.

Which parts of the stack get moved?

Every part which has the potential of corrupting the stack will get moved out of the safe stack. As soon as a variable on the stack gets passed by reference or one takes the address of this variable, the compiler will decide to allocate this on the second stack instead of the safe one.

As a result, any operation you do with those pointers, any modification you make on the memory (based on those pointers/references) can only effect the memory in the second stack. As one never gets a pointer which is close to the safe stack, the stack cannot corrupt the stack and the debugger can still read all functions on the stack to give a nice trace.

What is it actually used for?

The safe stack was not invented to give you better debugging experience, however, it is a nice side effect for nasty bugs. It's original purpose is as part of the Code-Pointer Integrity (CPI) Project, in which they try to prevent overriding the return addresses to prevent code injection. In other words, they try to prevent executing a hackers code.

For this reason, the feature has been activated on chromium and has been reported to have a <1% CPU overhead.

How to enable it?

Right now, the option is only available in the clang compiler, where one can pass -fsanitize=safe-stack to the compiler. A proposal was made to implement the same feature in GCC.

Conclusion

Stack corruptions can become easier to debug when safe stack is enabled. Due to a low performance overhead, you can even activated by default in your build configuration.