Buffer-Size and Buffer Output Annotations
A variable-sized object is any object that does not carry its own size with it. Many bugs in code, particularly security bugs, are caused by buffer overflows in which a variable-sized object is being passed between functions. The following annotations can be used to express the size of buffers:
_ecount(size)
_bcount(size)
_full(size)
_part(size, length)
These annotations provide information that ensures that neither the calling function nor the called function can access data outside the bounds of the buffer, but the annotations can also express the difference between available memory and initialized memory, so that access to uninitialized memory can be detected.
In C, buffers are simply arrays of something. When you describe the size of a buffer in your code, the size can be measured in two ways: as the number of bytes in the buffer or the number of elements in the buffer. For arrays of anything other than char, the size in elements differs from the size in bytes. In most cases--even for arrays of char, the size in elements is more useful and easier to express.
Note The sizes of wide character strings such as wchar_t are usually expressed in elements, not bytes. UNICODE_STRING is a notable exception.
Fixed-Size Buffer Annotations
The ecount(size) and bcount(size) partial annotations are used to express the size of a buffer. Use ecount(size) to express the size of a buffer as a number of elements. Use bcount(size) to express the size of the buffer as a number of bytes. The size parameter can be any general expression that makes sense at compile time. It can be a number, but it is usually the name of some parameter in the function that is being annotated.
The following code example of the memset function shows a typical buffer annotation:
void * memset(
__out_bcount(s) char *p,
__in int v,
__in size_t s);
In this example, the __out_bcount(s) annotation specifies that the content of the memory at p is set by the function and that the value of s is the number of bytes to be set. Nothing in the C source code tells the compiler that p and s are related in this way. The annotation provides this useful information.
With this information provided by the annotation, PFD can confirm that memset never accesses past the end of the buffer--that is, it never accesses more than s bytes into the buffer. Often, PFD can also check that the value of p+s is within the declared bounds of the array when memset is called. In this case, the buffer size is expressed in bytes because that is what memset expects.
Compare memset with a similar function, wmemset:
wchar_t * wmemset(
__out_ecount(s) wchar_t *p,
__in wchar_t v,
__in size_t s);
This example uses __out_ecount to indicate that s is represented in elements of the wchar_t type. If some incorrect code called this function with a byte count--which is an easy mistake to make--the value of s is likely to be twice the size that it should be. With the __out_ecount annotation, PFD has a good chance of detecting a buffer overrun in the caller and identifying a probable bug.
Note that for __in parameters, the definition of "valid" requires that the whole parameter being passed must be initialized. This also applies to arrays, which are passed by reference. Thus, when you use __in for a parameter that is an array, the whole array must be initialized up to the limit specified by _bcount or _ecount. For details about how this applies to null-terminated strings, see String Annotations.
Output Buffer Annotations
The _bcount and _ecount annotations are sufficient to describe __in buffers that are not modified or __out buffers that are fully initialized. For buffers that are partially initialized and that might have the initialized portion extended or reduced in place, you can combine these annotations with the _part and _full modifiers:
The _full modifier applies to the entire buffer. For an output buffer, the _full modifier indicates that the function initializes the entire buffer. For an input buffer, the _full modifier indicates that the buffer is already initialized, although this is redundant with other annotations.
The _part modifier indicates that the function initializes part of the buffer and explicitly indicates how much.
When you combine these modifiers with __inout buffers and _full(size) or _part(size, length**)** partial annotations, you can use them to represent the "before" and "after" sizes of a buffer. Size and length can be constants, or they can be parameters of the function being annotated. The following examples show the use of size and length in buffer annotations:
__inout_bcount_full(cb) describes a buffer that is cb bytes in size, is fully initialized at entry and exit, and might be written to by this function.
__out_ecount_part(count, *countOut**)** describes a buffer that is count elements in size and is to be partially initialized by this function. The function indicates the number of elements it initialized by setting *countOut.
Send comments about this topic to Microsoft
Build date: 5/3/2011