Preshing on ProgrammingPreshing on Programming

The Cost of Buffer Security Checks in Visual C++

The Buffer Security Check (/GS) compiler option is enabled by default in Release. It catches buffer overruns like the following:

void main()
{
    char buf[16];
    strcpy(buf, "This string is too long!"); // Buffer overrun
    puts(buf);
}

If you run the above program from the debugger, it pops up the following error message:

Outside the debugger, the program will simply crash before it can do further damage.

This compiler option is meant to catch overruns of fixed-size arrays located on the stack. When enabled, the compiler emits a few extra instructions at the start of the function, to insert a “security cookie” on the stack, and a few extra instructions before the return, to check that the security cookie hasn’t changed.

Arguably, this is essentially a Debug check. So why did Microsoft enable this compiler option by default, even in Release, starting with Visual C++ 2003? Because this type of programming error is at the heart of so many well-known exploits, it’s not considered a mere bug, but a security vulnerability.

The problem is that the stack contains a lot of other sensitive data, like the return address to the previous function. Here is what stack memory looks like in our small sample program, immediately before the strcpy happens:

And here’s what it looks like immediately after:

The return address was overwritten! Now suppose this program was some kind of network service, and the string being copied came from user input. Without the security cookie in place, a hacker could make the program jump to whatever address he wants, and compromise the machine in some way.

I’ve never worked at Microsoft, but sometime between 1999 and 2003, the company must have decided to place more emphasis on security across all Microsoft products. Those were the years that brought the infamous Nimda, Melissa and Code Red viruses. Code Red, in particular, exploited a stack buffer overrun in Microsoft’s IIS web server – exactly the kind of exploit which the Buffer Security Check would have prevented! Shortly after that, Visual C++, already a very mature product, slipped this feature into its default Release configuration.

Note that Microsoft has inserted other safeguards against stack buffer overflows as well. You might have noticed compiler warnings suggesting that you use less-standard, but safer functions, like strcpy_s, instead. Address Space Layout Randomization, an OS-level feature, also makes this kind of attack more difficult, and is even available on other platforms like Linux and Mac OS X.

In any case, it’s easy enough to turn this feature off: project Properties → C/C++ → Code Generation → Buffer Security Check.

Performance Measurements

I tested this compiler option on wordcount.exe, a benchmarking application used for Hash Table Performance Tests. I used the same testing procedure described in my previous post, The Cost of Enabling Exception Handling, so feel free to review that post to better understand this chart:

In some tests, the Buffer Security Check had no effect. Counterintuitively, a few tests became a little bit faster. (See the end of this post for some theories about that.) In other tests, the extra overhead carried a heavy cost. As you might expect, it depends how often the test creates C-style arrays as local variables. In particular, it slowed down the performance of the JudySL Array test by more than 10%. This guy on Stack Overflow also found a 10% performance impact in his own test suite.

I think it’s a wise move by Microsoft to make this a default compiler option. But if you’re developing a Visual C++ application which is not a network service, and you’re striving for maximum performance, such as in a game, editor, or benchmarking tool like wordcount.exe – and it’s unlikely to be targeted by hackers – you might consider this feature a performance pitfall, and choose to disable it.