As y’all know, the Visual C++ /GS compiler flag adds prolog and epilog code to certain functions to help detect some classes of stack based buffer overruns at runtime. In VC++ 2005, the code looks like this:

Function prolog

      sub   esp, 8

      mov   eax, DWORD PTR ___security_cookie

      xor   eax, esp

      mov   DWORD PTR __$ArrayPad$[esp+8], eax

      mov   eax, DWORD PTR _input$[esp+4]

Function epilog

      mov   ecx, DWORD PTR __$ArrayPad$[esp+12]

      add   esp, 4

      xor   ecx, esp

      call  @__security_check_cookie@4

      add   esp, 8

 

__security_cookie is a pseudo-random number created when the image loads.

In the overall scheme of things, we see little if any performance degradation because of the /GS.  But there are a set of heuristics you should be aware when compiling with /GS.  In short, the compiler does not apply the stack cookie code to all functions; it applies a set of heuristics to determine which functions should get the stack cookie treatment. Read the /GS documentation <http://msdn2.microsoft.com/en-US/library/8dbf701c.aspx> for an explanation of the compiler heuristics.

In Visual C++ 2005 SP1, we added a new pragma that makes the -GS flag much more aggressive. The syntax is:

#pragma strict_gs_check([push,] on )
#pragma strict_gs_check([push,] off )
#pragma strict_gs_check(pop)

Personally, I enable this at the top of any source code file that includes functions highly susceptible to attack or code that handles untrusted data.

Here’s a quick code sample. The code has a buffer overrun copying an array to a local array, but when it is compiled with /GS, no cookie is inserted in the stack because the array data type is an unsigned integer. Adding the strict_gs_check pragma forces the stack cookie into the function stack.

#pragma strict_gs_check(on)

unsigned int * ReverseArray(__in_ecount(cData) unsigned int *pdwData, size_t cData) {
   unsigned int dwReversed[20];  // *** This buffer is subject to being overrun!! ***
   // Reverse the array into a temporary buffer

   for (size_t j=0, i = cData; i ; --i, ++j) 
      dwReversed[j] = pdwData[i];  // *** Possible buffer overrun!! ***

      
   // Copy temporary buffer back into input/output buffer
   for (size_t i = 0; i < cData ; ++i) 
      pdwData[i] = dwReversed[i];

    return pdwData;
}

Also, functions with stack-based buffers that are less than 4-bytes long will also get a cookie.

As a result of applying this pragma to some sample code, there was an increase from 39 to 56 functions protected by the GS cookie out of a total of 761 functions. For high-attack surface components, which often listen on the network, the extra performance overhead due to such a small number of extra GS checks is negligible, but the extra defensive layer is valuable.