Get on-the-go access to the latest insights featured on our Trustworthy Computing blogs.
Hello all – Dave here…
Now that RSA is over and things are somewhat back to normal, we can get back to the business of talking about Visual Studio features as they relate to the SDL. Here we present the fourth installment of the series by Thomas Garnier of the Security Science team talking about Visual Studio and proper C++ class initialization…
In previous posts we introduced the /sdl compiler switch and described our approach when designing and implementing additional security features in Visual Studio 2011.
This post describes another feature enabled by the /sdl compiler switch: in specific cases it will initialize all member pointers of a class. This feature addresses a common mistake made by developers when creating C++ classes.
Creating C++ classes requires the developer to handle the class initialization correctly. A classic mistake is forgetting or failing to initialize a member pointer that will be used or freed in the destructor.
The following example highlights how a class initialization might fail leaving a member pointer uninitialized. This problem was seen in multiple MSRC cases such as MS10-071, MS10-053 and MS09-051.
This code assumes that the allocation will succeed and initialize the p variable. An attacker might influence the size or state of the process forcing a particular failure path to be taken. In this example it would free an uninitialized pointer which can lead to code execution.
We have seen different approaches to make this initialization process easier. For example, a developer might use a custom operator “new" to allocate and initialize memory. However the custom operator is not called if the variable is declared locally, as a member of another class (not being a pointer) or if the class is inherited.
Ensuring a class is always properly initialized can be a challenge, especially for large and complex code bases.
In reviewing MSRC cases involving uninitialized class members, we noted that uninitialized pointers are the most common issue that could lead to code execution.
The following examples highlight how the compiler generates class creation code into assembly and how the /sdl compiler switch can change the generated code to initialize class pointers avoiding the common mistake described earlier.
Creating an object for the previous example via the following code:
Generates this assembly by default (without /sdl) on x86:
With the /sdl compiler switch enabled, when creating a class the Visual Studio 11 Developer Preview compiler will generate a new function called A::__autoclassinit(). This function is responsible for the initialization of class member pointers. It will also call the compiler-generated class initialization functions of any base classes or member variable of a class type. For our example the A::__autoclassinit() function is called before the call to the constructor:
In this example the A::__autoclassinit() function would initialize the variable p, call B::__autoclassinit() for the variable o and not touch n:
The compiler-generated class initialization function is called as long as:
- The object is not allocated using a custom (user defined) operator new
- The object is not allocated as part of an array (for example “new A[x]”)
- The class is not managed or imported
To be initialized by the compiler-generated class initialization function, a member has to respect the following rules:
- The member is a pointer
- The member is not a property or constant
Automated initialization of class member pointers demonstrates the capabilities offered by the compiler to mitigate complex security problems.
We hope you will find this functionality of the /sdl compiler switch useful and encourage you to try it. You can find more information about the Security Development Lifecycle (SDL), including articles, tools and more on this website.
Thomas Garnier, MSEC Security Science.
Hmm, why did you use 0, and not a known-crashing error value like the debugging patterns - 0xCDCDCDCD in this case, I think? This sounds like it could be to easily accidentally dependable on, for example the developer would probably not guess that it's not null-initialized in the new array case. I'm fine with languages that guarantee full zero-init, since that's dependable (and calloc() is pretty damn cheap).
It is a good question and we considered using another static value while creating this feature. We decided to use the 0 value as we wanted to follow what the developers were expecting from the initialization of an object. In this specific case we think this approach is better because it protects most C++ object while not introducing instability or change in expected behavior. Saying that we will consider on future versions if we can give the user a choice helping him to identify these issues more effectively.
sii yas y facedook si las y gamias si errross y wedd