Hardening Stack-based Buffer Overrun Detection in VC++ 2005 SP1


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.


 

Comments (9)

  1. Yoink says:

    Do Microsoft care to explain what ‘If a parameter is used only in ways that are less likely to be exploitable in the event of a buffer overrun.’ means?

  2. michael_HOWARD says:

    The sample code above shows the main one – using non-chars as the buffer element type

  3. ddebug says:

    Is this correct that /GS can make copies of vulnerable parameters on stack, and thus increase stack usage much more and less predictably than we could guess? (8 bytes per call frame)

    Is there any easy way to know when the compiler makes big allocations on stack? This may be important for kernel mode, where stack is limited.

  4. michael_HOWARD says:

    ddebug,

    yes it’s true, but in our experience, it’s really not a big issue. We saw now perf problems or stack exhaustion.

  5. Does using this flag enable /GS cookies for the LoadAniIcon function which was responsible for the recent ANI vulnerability? It was a perfect example of a function which should have been protected by /GS, but the compiler failed to insert the cookie because the function was overwriting a structure on the stack, instead of an array.

    By the way, what exact version of VC2005 was used to compile Vista? Is it the same one that’s included in the Vista release of the Windows SDK, or a private build of the compiler?

  6. michael_HOWARD says:

    Hello Alexander

    re: /GS – yes, the #pragma would add the cookie to this func.

  7. Michael Howard here. A core tenet of the SDL is to take and incorporate lessons learned when we issue

  8. anonymous says:

    I have VS2005 SP1 running, but the compiler complains that this pragma simply doesn’t exist. What now?