"Strong" stack protection for GCC

LWN.net needs you! Without subscribers, LWN would simply not exist. Please consider signing up for a subscription and helping to keep LWN publishing

Stack buffer overflows are a longstanding problem for C programs that leads to all manner of ills, many of which are security vulnerabilities. The biggest problems have typically been with string buffers on the stack coupled with bad or missing length tests. A programmer who mistakenly leaves open the possibility of overrunning a buffer on a function's stack may be allowing attackers to overwrite the return pointer pushed onto the stack earlier. Since the attackers may be able to control what gets written, they can control where the function returns—with potentially dire results. GCC, like many compilers, offers features to help detect buffer overflows; the upcoming 4.9 release offers a new stack-protection mode with a different tradeoff between security and performance impact.

GCC has supported stack protection for some time. It currently supports two different types of stack protection. Recently, Google engineers have come up with another style that tries to chart a middle course between the two existing options. It has made its way into GCC 4.9 (expected later this year) and the upcoming 3.14 kernel has support for building with that option.

The basic idea behind stack protection is to push a "canary" (a randomly chosen integer) on the stack just after the function return pointer has been pushed. The canary value is then checked before the function returns; if it has changed, the program will abort. Generally, stack buffer overflow (aka "stack smashing") attacks will have to change the value of the canary as they write beyond the end of the buffer before they can get to the return pointer. Since the value of the canary is unknown to the attacker, it cannot be replaced by the attack. Thus, the stack protection allows the program to abort when that happens rather than return to wherever the attacker wanted it to go.

There is a downside to using canaries. The value must be generated and checked, which takes some time, but more importantly there must be code added to handle the canary for each function that is protected that way. That extra code results in some level of performance degradation, perhaps mostly due to a larger cache footprint. For this reason, it can make sense to restrict stack protection to a subset of all the functions in a program.

So the question has always been: "Which functions should be protected?" Putting stack protection into every function is both overkill and may hurt performance, so one of the GCC options chooses a subset of functions to protect. The existing -fstack-protector-all option will protect all functions, while the -fstack-protector option chooses any function that declares a character array of eight bytes or more in length on its stack. Some distributions have lowered that threshold (e.g. to four) in their builds by using the --param=ssp-buffer-size=N option.

That "character array" test catches the most "at risk" functions, but it leaves a number of other functions behind. As Kees Cook pointed out in a recent blog post, the Google Chrome OS team had been using -fstack-protector-all since the team is "paranoid", but a new -fstack-protector-strong option has been developed to broaden the scope of the stack protection without extending it to every function in the program.

In addition to the protections offered by -fstack-protector , the new option will guard any function that declares any type or length of local array, even those in structs or unions. It will also protect functions that use a local variable's address in a function argument or on the right-hand side of an assignment. In addition, any function that uses local register variables will be protected. According to Cook, Chrome OS has been using -fstack-protector-strong (instead of protecting all functions) for ten months or so.

During the 3.14 merge window, Linus Torvalds pulled Cook's patches to add the ability to build the kernel using the strong stack protection. In Ingo Molnar's pull request (and Cook's post), the results of using strong protection on the kernel were presented. The kernel with -fstack-protector turned on is 0.33% larger and covers 2.81% of the functions in the kernel. For -fstack-protector-strong , those numbers are an increase of 2.4% in code size over an unprotected kernel, but 20.5% of the functions are covered.

The CONFIG_CC_STACKPROTECTOR_STRONG kernel configuration option adds the strong protection, while the CONFIG_CC_STACKPROTECTOR option for the "regular" protection has been renamed to reflect that: CONFIG_CC_STACKPROTECTOR_REGULAR . The default CONFIG_CC_STACKPROTECTOR_NONE does just what its name would imply.

While stack protection certainly isn't a panacea for security woes, it will catch a significant portion of real-world attacks. Having an option that strikes a balance between the ultra-paranoid "all" and the regular variant (not to mention the wide-open "none" option) is likely to catch more bugs—and attack vectors. We will likely see some of the more security-conscious distributions building their user-space programs and kernels with the "strong" option moving forward.