Tuesday, February 17, 2015

SAL to SAL2 Porting Guide

I have written before that you should use SAL in your Windows C/C++ code.  It allows to you do neat things like use Microsoft's static analysis and kill lots of common bugs at compile time.  Whereas C can be ambiguous how say a pointer parameter is used, SAL can make the intended use of parameters and struct members very crisp.  A: if it is hard to figure out what SAL annotation defines your parameter's use in the function, you are probably doing it wrong.  In general, you should make your functions use the parameters in straightforward ways.  B: Likewise, there is always a correct set of SAL annotations for a parameter's behavior, so don't cop out and figure it out.  If you are still confused, refer to A.

Why port to SAL2?  Because 2 > 1 obviously.  SAL2 does add some new functionality.  You should not be lazy and just use SAL2 in new code, and clean up old code to use SAL2 while you are in there working on it.  Should you go back and fix old code to use SAL2?  It is up to you.  It has some new functionality, however, SAL1 will continue to work just fine.

Const Protection, Use It

This isn't SAL, just regular C.  Use const protection.  Parameters that should not be modified should be const protected.  For instance: _In_ const char *p is more correct than _In_ char *p if the p buffer should not be modified.  Please use const correctly for parameters and struct members.

Here is a table for the basics of SAL2.

Here is my version with SAL to SAL2 notes.

Pointer Parameters

For the annotations in the following table, when a pointer parameter is being annotated, the analyzer reports an error if the pointer is null. This applies to pointers and to any data item that's pointed to.

SAL 1
SAL 2
Description
__in
_In_
Annotates input parameters that are scalars, structures, pointers to structures and the like. Explicitly may be used on simple scalars. The parameter must be valid in pre-state and will not be modified.
__out
_Out_
Annotates output parameters that are scalars, structures, pointers to structures and the like. Do not apply this to an object that cannot return a value—for example, a scalar that's passed by value. The parameter does not have to be valid in pre-state but must be valid in post-state.
__inout
_Inout_
Annotates a parameter that will be changed by the function. It must be valid in both pre-state and post-state, but is assumed to have different values before and after the call. Must apply to a modifiable value.
__in_z
_In_z_
A pointer to a null-terminated string that's used as input. The string must be valid in pre-state. Variants of PSTR, which already have the correct annotations, are preferred.  <= This is a pet annoyance of mine, don't be the guy that does _In_z_ PCWSTR.
__inout_z
_Inout_z_
A pointer to a null-terminated character array that will be modified. It must be valid before and after the call, but the value is assumed to have changed. The null terminator may be moved, but only the elements up to the original null terminator may be accessed.
__in_ecount(s)
__in_bcount(s)
_In_reads_(s)
_In_reads_bytes_(s)
A pointer to an array, which is read by the function. The array is of size s elements, all of which must be valid.
The _bytes_ variant gives the size in bytes instead of elements. Use this only when the size cannot be expressed as elements. For example, char strings would use the _bytes_ variant only if a similar function that uses wchar_t would.
__in_ecount_z(s) 
_In_reads_z_(s)
A pointer to an array that is null-terminated and has a known size. The elements up to the null terminator—or s if there is no null terminator—must be valid in pre-state. If the size is known in bytes, scale s by the element size.

_In_reads_or_z_(s)
A pointer to an array that is null-terminated or has a known size, or both. The elements up to the null terminator—or s if there is no null terminator—must be valid in pre-state. If the size is known in bytes, scale s by the element size. (Used for the strn family.)

_Out_writes_(s)
_Out_writes_bytes_(s)
A pointer to an array of s elements (resp. bytes) that will be written by the function. The array elements do not have to be valid in pre-state, and the number of elements that are valid in post-state is unspecified. If there are annotations on the parameter type, they are applied in post-state. For example, consider the following code.
C++
typedef _Null_terminated_ wchar_t *PWSTR;
void MyStringCopy(_Out_writes_ (size) PWSTR p1,
   _In_ size_t size,
   _In_ PWSTR p2);
In this example, the caller provides a buffer of size elements for p1. MyStringCopy makes some of those elements valid. More importantly, the _Null_terminated_ annotation onPWSTR means that p1 is null-terminated in post-state. In this way, the number of valid elements is still well-defined, but a specific element count is not required.
The _bytes_ variant gives the size in bytes instead of elements. Use this only when the size cannot be expressed as elements. For example, char strings would use the _bytes_ variant only if a similar function that uses wchar_t would.

_Out_writes_z_(s)
A pointer to an array of s elements. The elements do not have to be valid in pre-state. In post-state, the elements up through the null terminator—which must be present—must be valid. If the size is known in bytes, scale s by the element size.

_Inout_updates_(s)
_Inout_updates_bytes_(s)
A pointer to an array, which is both read and written to in the function. It is of size s elements, and valid in pre-state and post-state.
The _bytes_ variant gives the size in bytes instead of elements. Use this only when the size cannot be expressed as elements. For example, char strings would use the _bytes_ variant only if a similar function that uses wchar_t would.

_Inout_updates_z_(s)
A pointer to an array that is null-terminated and has a known size. The elements up through the null terminator—which must be present—must be valid in both pre-state and post-state. The value in the post-state is presumed to be different from the value in the pre-state; this includes the location of the null terminator. If the size is known in bytes, scale s by the element size.

_Out_writes_to_(s,c)
_Out_writes_bytes_to_(s,c)
_Out_writes_all_(s)
_Out_writes_bytes_all_(s)
A pointer to an array of s elements. The elements do not have to be valid in pre-state. In post-state, the elements up to the c-th element must be valid. If the size is known in bytes, scale s and c by the element size or use the _bytes_ variant, which is defined as:
C++
   _Out_writes_to_(_Old_(s), _Old_(s))
   _Out_writes_bytes_to_(_Old_(s), _Old_(s))
In other words, every element that exists in the buffer up to s in the pre-state is valid in the post-state. For example:
C++
void *memcpy(_Out_writes_bytes_all_(s) char *p1,
   _In_reads_bytes_(s) char *p2,
   _In_ int s);
void * wordcpy(_Out_writes_all_(s) DWORD *p1,
   _In_reads_(s) DWORD *p2,
   _In_ int s);

_Inout_updates_to_(s,c)
_Inout_updates_bytes_to_(s,c)
A pointer to an array, which is both read and written by the function. It is of size s elements, all of which must be valid in pre-state, and c elements must be valid in post-state.
The _bytes_ variant gives the size in bytes instead of elements. Use this only when the size cannot be expressed as elements. For example, char strings would use the _bytes_ variant only if a similar function that uses wchar_t would.

_Inout_updates_all_(s)
_Inout_updates_bytes_all_(s)
A pointer to an array, which is both read and written by the function of size s elements. Defined as equivalent to:
C++
   _Inout_updates_to_(_Old_(s), _Old_(s))
   _Inout_updates_bytes_to_(_Old_(s), _Old_(s))
In other words, every element that exists in the buffer up to s in the pre-state is valid in the pre-state and post-state.
The _bytes_ variant gives the size in bytes instead of elements. Use this only when the size cannot be expressed as elements. For example, char strings would use the _bytes_ variant only if a similar function that uses wchar_t would.

_In_reads_to_ptr_(p)
A pointer to an array for which the expression p – _Curr_ (that is, p minus _Curr_) is defined by the appropriate language standard. The elements prior to p must be valid in pre-state.

_In_reads_to_ptr_z_(p)
A pointer to a null-terminated array for which the expression p – _Curr_ (that is, p minus_Curr_) is defined by the appropriate language standard. The elements prior to p must be valid in pre-state.

_Out_writes_to_ptr_(p)
A pointer to an array for which the expression p – _Curr_ (that is, p minus _Curr_) is defined by the appropriate language standard. The elements prior to p do not have to be valid in pre-state and must be valid in post-state.

_Out_writes_to_ptr_z_(p)
A pointer to a null-terminated array for which the expression p – _Curr_ (that is, p minus_Curr_) is defined by the appropriate language standard. The elements prior to p do not have to be valid in pre-state and must be valid in post-state.