New Safety Rules in C++ Code Analysis

Avatar

Hwi-sung

In Visual Studio version 16.8 Preview 3we are adding few safety rules to C++ Code Analysis that can find some common mistakes, which can lead to bugs ranging from simple broken features to costly security vulnerabilities. These new rules are developed around issues discovered in production software via security reviews and incidents requiring costly servicing. Every shipping piece of software in Microsoft runs these rules as part of security and compliance requirements.

This blog post will introduce new rules related to VARIANT and its sibling types – such as VARIANTARG, or PROPVARIANT. To help with the new rules, we have built a code analysis extension, called VariantClear, that detects violations of these new rules in code It is named VariantClear because the primary rule it detects is about misuse of VariantClear function.

The VariantClear extension detects and reports the following warnings:

  • C33001VARIANT ‘var’ was cleared when it was uninitialized 
  • C33004VARIANT ‘var’, which is marked as Out was cleared before being initialized  
  • C33005: VARIANT ‘var’ was provided as an input or input/output parameter but was not initialized 

While Visual Studio version 16.8 Preview 3 already has the VariantClear extension included, it is not yet enabled by default. To enable this extension, please add the following lines either to your project file or to the Microsoft.CodeAnalysis.Extensions.props file under MSBuild\Microsoft\VC\v160 folder in the Visual Studio installation location:

If you want to add this to individual project file, add it after all other <PropertyGroup> elements:

<PropertyGroup Condition="'$(ConfigurationType)'!='Utility' and '$(ConfigurationType)'!='Makefile'">
    <EspXtensions Condition="'$(EnableVariantClear)'!='false'">VariantClear.dll;$(EspXtensions)</EspXtensions>
</PropertyGroup>

If you want to modify your Visual Studio installation, you can add this to the Microsoft.CodeAnalysis.Extensions.props file, after the similar element for HResultCheck:

<EspXtensions Condition="'$(EnableVariantClear)'!='false'">VariantClear.dll;$(EspXtensions)</EspXtensions>

Please note that this will likely be overwritten if you repair or reinstall Visual Studio, or upgrade to a later release. Please stay tuned for update when we have this extension enabled in Visual Studio.

VariantClear Rules 

VARIANT is a very convenient structure, allowing exchange of many different types of data using a single struct type. At any given time, it can hold either one of the alternative types, or no value. Type of the contained data or the fact that it contains no value is identified by the VARIANT::vt member. 

A VARIANT object needs to be explicitly initialized before use or passed to some other code. Otherwise, this will cause random data to be accessed and used, causing different problems depending on what is accessed and how it is used. 

A VARIANT object also needs to be cleared when it is no longer needed. Otherwise, it can leave some resources behind, leaking resources or letting others mistakenly access and use the resource after its intended lifetime. 

Initialization of a VARIANT object is usually done through calling VariantInit function. Clean up of a VARIANT object is mostly done through calling VariantClear function. 

There are some wrapper types for VARIANT struct to make it easier and safer to use, e.g. CComVariant  and _variant_t. Their default constructors initialize the instances being created and mark them as having no value, usually by calling VariantInit, passing the current instance. Their destructors clear the instances being destructed and mark them as having no value, usually by calling VariantClear, passing the current instance. 

VariantClear rules try to enforce the general rules of proper initialization of VARIANT instances before their use, including cleaning them up. 

Warning C33001 

This warning is triggered when an uninitialized VARIANT is passed into an API that clears a VARIANT such as VariantClear. These APIs expect the VARIANT is initialized before they can be cleared. Unfortunately, developers often forget this step.

Here is a simplified example: 

#include <Windows.h> 

HRESULT foo(bool some_condition) 
{
    VARIANT var; 
  
    if (some_condition) 
    { 
        //... 
        VariantInit(&var); 
        //... 
    } 
    VariantClear(&var);     // C33001 
}

This code will trigger a C33001 warning because the VARIANT var is conditionally initialized only if some_condition is true. If the condition is false, it will not be initialized when it is passed to VariantClear function. To fix this problem, we have to make sure that we are calling VariantClear only for the VARIANTs that have been initialized: 

#include <Windows.h> 

HRESULT foo(bool some_condition) 
{ 
    VARIANT var; 
  
    if (some_condition) 
    { 
        //... 
        VariantInit(&var); 
        //... 
        VariantClear(&var);     // C33001 
    } 
}

Warning C33004 

This warning is triggered when a VARIANT parameter with _Out_ SAL (source-code annotation language) annotation, which may not be to be initialized on input, is passed to an API such as VariantClear that expects an initialized VARIANT. 

A parameter that is annotated as _Out_ is not required to have been initialized when calling the function. It will be initialized upon return from the function. For more details of SAL annotations, please refer to SAL Annotations. 

During code analysis, an _Out_ VARIANT parameter is assumed to be uninitialized, to be on the safer side. If this parameter is passed to a function such as VariantClear that expects an initialized VARIANT object, it will try to clean up or use a random type of data, possibly at random memory location. Here is a simplified example: 

#include <Windows.h> 
  
HRESULT t2(_Out_ VARIANT* pv) 
{ 
    // ...... 
    VariantClear(pv);   // C33004. pv is assumed uninitialized. 
    // ...... 
  
    return S_OK; 
}

To fix this problem, we have to make sure to initialize the _Out_ VARIANT parameter before using it or passing it to another function that expects an initialized VARIANT instance: 

#include <Windows.h> 
  
void t2(_Out_ VARIANT* pv) 
{ 
    VariantInit(pv); 
    // ...... 
    VariantClear(pv);   // OK 
    // ...... 
}

Warning C33005 

This warning is triggered when an uninitialized VARIANT is passed to a function as input only or input/output parameter – for example, a parameter of const VARIANT* type. Here is an example: 

#include <Windows.h> 
  
void bar(VARIANT* v);   // v is assumed to be input/output 
  
void foo() 
{ 
    VARIANT v; 
    bar(&v);            // C33005 
    // ...... 
    VariantClear(&v);   // OK, assumed to be initialized by bar 
}

Please note that the checker assumes a function that takes a non-const VARIANT* parameter would initialize the VARIANT object upon return from the function, to avoid generating noisy warnings. 

Again, to fix this problem, we simply need to make sure to initialize the VARIANT object before passing it to another function as an input-only or input-output parameter: 

#include <Windows.h> 
  
void bar(VARIANT* v);   // v is assumed to be input/output 
  
void foo() 
{ 
    VARIANT v; 
    VariantInit(&v); 
    bar(&v);            // OK 
    // ...... 
    VariantClear(&v);   // OK, assumed to be initialized by bar 
} 

With the understanding of C33005 rule, it should be clearer why C33004 is reported only for an output-only (that is, annotated with _Out_ SAL annotation) parameter. For an input-only or input-output parameter, passing an uninitialized VARIANT will be a violation of rule C33005. 

Enabling new rules in Visual Studio 

You can enable these rules in Visual Studio as follows by selecting different ruleset for your project: 

Rule ID Extension Native Minimum Rules Native Recommended Rules All Rules 
C33001 VariantClear X X X 
C33004 VariantClear   X X 
C33005 VariantClear   X X 

 

Give us your feedback 

Check out these newly added rules and let us know if they help you write safer C++. Stay tuned as we add more safety rules in future releases of Visual Studio. 

Download Visual Studio 2019 version 16.8 Preview 3 today and give it a try. We would love to hear from you to help us prioritize and build the right features for you. We can be reached via the comments below, Developer Community, and Twitter (@VisualC). The best way to file a bug or suggest a feature is via Developer Community. 

 

7 comments

Leave a comment