A while back I wrote a blog post explaining the Standard Annotation Language (SAL) which is a technology we use to help static analysis tools find more bugs, including security vulnerabilities, in C and C++ code. If you look closely at VC++ 2005 and VC++ 2008, you’ll notice that almost all function prototypes are SAL annotated, which means you get the benefit of all the SAL work we did. But you might have also notice that the annotation style between the two compiler versions is different.
For example, in Visual C++ 2005, realloc() is annotated like this:
__checkReturn __bcount_opt(_NewSize) void * __cdecl realloc( __in_opt void * _Memory, __in size_t _NewSize);
But in VC++ 2008, realloc() is annotated like this:
_Check_return_ _Ret_opt_bytecap_(_NewSize) void * __cdecl realloc( _In_opt_ void * _Memory, _In_ size_t _NewSize);
|SAL Macro||SAL Primitives|
void Foo( __in_bcount(cb) BYTE* pBuf, size_t cb );
__declspec(“SAL_readableTo(byteCount(“”cb””))”) BYTE* pBuf, size_t cb );
void Foo( _In_bytecount_(cb) BYTE* pBuf, size_t cb );
[SA_Pre(Deref=1,Access=SA_Read)] BYTE* pBuf, size_t cb );
Aren’t you happy we created macros for the low-level primitives!? You should never have to use the low-level primitives in your code: the table is to show you why the two SAL formats got their names.
So why a new SAL syntax? I have good news and really good news. First, the good news: other than a simple macro syntax change, there is not a lot new to learn in part because the macros are similar (not identical, however) and the major difference, the low-level primitives, are abstracted away.
Now for the really good news. Attribute SAL is much more rigorous than declspec SAL, which means analysis tools can find more bugs with lower false positives (‘noise’). For example, declspec SAL is often silent in the face of an incorrect annotation.
The introduction of attribute SAL does not mean declspec SAL is dead, but it does mean that we will not be investing any more resources into declspec SAL, all our energy improving SAL and our analysis tools use of SAL will be in attribute SAL. At a pragmatic level, this means:
· If you have already invested in using declspec SAL you should migrate over to attribute SAL as time allows, and use new attribute SAL for new functions. Both syntaxes can co-exist.
· If you have never used SAL, you should use attribute SAL. As far as you’re concerned, declspec SAL never existed.
One noticeable difference in macro names is the use of declspec SAL’s “count” and attribute SAL’s “cap” and “count.” The former is a buffer size in elements or bytes, but the latter two are the buffer’s writing capacity and the size of the buffer for reading, respectively.
An important addition to attribute SAL is _Printf_format_string_ which can be used to find many printf-related format-matching ills.
The following table shows some of the major differences:
|declspec SAL||Attribute SAL|
|Syntax||Loose, allows macros in places they don’t make sense||Strict, annotations can be only put on parameters and return values|
|Consistency checks||Few, allows wrong macros||Many, exhaustive set of warnings for wronginconsistent annotations|
|Constant expressions buffer sizes||Simple expressions only||Fully supported including templates, but requires different macros.|
|Return values||Loose syntax and consistency rules allow the use of ‘__out’ family||Special set of macros for return values required|
|Naming consistency||Overloaded use of ‘count’ for writable and readable extent. Hard to understand _full and _part postfixes||Consistent use of ‘cap’ (capacity) for writable extent and ‘count’ for readable extent|
As noted in the table above, there is one minor drawback to using attribute SAL. If you use constant expressions as count or cap arguments, you must use a special set of macros, which is a little less elegant than declspec SAL:
void Foo( _In_count_c_( 8 ) int* rgInt );
void Foo( __in_count( 8 ) int* rgInt );
Note the _c_ portion of the attribute syntax, which is not needed when using declspec macros. With that said, attribute syntax supports accept any C++ conformant constant expression including enums and template arguments, but decspec SAL supports only simple expressions.
To put his altogether, let’s look at some simple code, and see how the VC++ 2008 /analyze static analysis performs when faced with the different SAL types.
First, declspec SAL:
When compiled with /W4 /analyze, the compiler gives us:
warning C6309: Argument '1' is null: this does not adhere to function specification of 'FuncThree' warning C6309: Argument '1' is null: this does not adhere to function specification of 'FuncFour' warning C6387: 'argument 1' might be '0': this does not adhere to the specification for the function 'FuncThree': Lines: 14, 15, 16, 17 warning C6387: 'argument 1' might be '0': this does not adhere to the specification for the function 'FuncFour': Lines: 14, 15, 16, 17, 18, 19, 21, 22
Now, let’s take the same code, but decorate the function prototypes with attribute SAL rather than declspec SAL.
warning C6273: Non-integer passed as parameter '2' when integer is required in call to 'FuncTwo': if a pointer value is being passed, %p should be used warning C6064: Missing integer argument to 'FuncTwo' that corresponds to conversion specifier '3' warning C6309: Argument '1' is null: this does not adhere to function specification of 'FuncThree' warning C6309: Argument '1' is null: this does not adhere to function specification of 'FuncFour' warning C6001: Using uninitialized memory 'b': Lines: 14, 15 warning C6387: 'argument 1' might be '0': this does not adhere to the specification for the function 'FuncThree': Lines: 14, 15, 16, 17 warning C6001: Using uninitialized memory 'blah': Lines: 14, 15, 16, 17, 18, 19 warning C6387: 'argument 1' might be '0': this does not adhere to the specification for the function 'FuncFour': Lines: 14, 15, 16, 17, 18, 19, 21, 22
As you can see, using attribute SAL found many more code bugs, and all of them are real. I’ll let you sift through the list to see what attribute SAL found over and above declspec SAL! There are some duplicate bugs, however.
If you want to learn more about SAL, I would recommend you simply open sal.h and read the comments and examples.
The Rosetta Stone
Below is a partial Rosetta Stone to help you convert between the two SAL syntaxes if you need to do so.
I would like to thank Hannes Ruescher (Dev Mgr in Office,) Dave Bartolomeo (Principal Software Design Engineer in Visual Studio) and Bruce Dawson (Principal Software Design Engineer in Windows) for their gracious help providing core content for this document.