Share via


Overview of Annotations for Drivers

Annotations are tokens that you add to your source code (such as __in, __out, and __inout) that provide developers and the static analysis tools with additional information about a function and its parameters, and their intended purpose. Annotations are like comments that you add to your code and are ignored by the compiler but are used by the static analysis tools. The use of annotations helps improve developer effectiveness, helps improve the accuracy of the results from static analysis, and allows the tools to better determine whether a particular bug exists.

For example, if you add an annotation to indicate the size of a buffer parameter, PREfast for Drivers (PFD) can check for conditions that would cause a buffer overrun. In addition, when you add annotations to your code, you have the opportunity to examine some of the assumptions that you have made about the functions and their parameters. It is often this close examination that helps you discover potential problems in your code.

How Annotations Help in Code Analysis

  • Annotations provide PFD with additional information about the global state and the work that is performed by functions that are called by the function that is currently being analyzed. Additionally, PFD provides more specific information about the roles and possible values of function parameters.

  • Annotations help PFD perform more extensive checking of particular kinds of functions. Several PFD warnings about incompatible function types go well beyond what the compiler requires. Part of the annotation system is designed so that these warnings can be suppressed easily (with the use of #pragma directive). As an additional benefit, PFD can enable additional checking after the type of the function is known.

Where to Place Annotations

You place annotations on the driver-supplied function prototypes and function definitions in your driver source code. You can use as many or as few annotations as you need. The annotations can describe entire functions, the function return values, or the function parameters. You can also add annotations to typedef declarations, so that you can gain the benefit of those annotations whenever you declare something of that type. A limited number of annotations can also be applied to structure or class declarations.

The following example shows the memset function. This example shows placement of the annotations __in, __out, and _bcount(size). The annotations __in and __out that indicate whether a parameter to a function is expected to be valid on input (when the function is called) or on output (when the function returns). The _bcount(size) is a partial annotation that indicates the size of the buffer in bytes. This buffer annotation is combined with the __out annotation. This combination indicates that the memset function initializes and writes to the buffer p and that the buffer is valid when the function returns.

void * memset(
 __out_bcount(s) char *p,
 __in int v, 
 __in size_t s);

General Purpose and Driver-Specific Annotations

The annotations for drivers are an extension of the general purpose source code annotations. The general purpose source code annotations, also known as header annotations, are based on the standard source code annotation language (or SAL). If you examine the system-supplied header files, you will notice some of these annotations (__in, __out, __inout) on the parameters of the system-supplied functions. The annotations appear because Microsoft has added the annotations to make sure that you are calling the DDI correctly. The general purpose annotations are defined in WDK\inc\api\Specstrings.h and can be applied to driver and general user-mode and kernel-mode code. The annotations for drivers are defined in WDK\inc\ddk\Driverspecs.h. The driver annotations differ from the general purpose annotations in that they are designed specifically to describe the behavior and usage in drivers and in kernel-mode code.

Annotations and Function Prototypes

You can think of annotations as an extension of the concept of function prototypes in the C language. In C, a function prototype establishes the function return type and the types and numbers of parameters that the function takes. The use of function prototypes can help the compiler catch mismatch errors in the number and type of arguments that are passed in a function call. However, prototypes do not provide enough information about the intended use of a function for static analysis tools to identify or eliminate possible errors.

For example, C passes parameters by value, so parameters themselves are always input parameters to a function. However, C can pass a pointer by value, so it is not possible to tell just by looking at a function prototype whether the value that is passed by using a pointer is intended as input to the function, output from the function, or both.

Without annotations, PREfast for Drivers (PFD) cannot determine whether the parameter should be initialized before the call, and therefore it can only flag an uninitialized parameter as a potential problem. However, if the function has annotations and a parameter is marked __in, PFDcan check to be sure that the parameter is initialized before the call. If a parameter is marked __out, PFD does not have to check that it is initialized before the call, but can assume that it is initialized after the call (and thus is safe to use as an __in parameter to a subsequent function). PFD can check parameters with __in, __out, and __inout annotations and report an error if it finds that uninitialized values are used incorrectly.

 

 

Send comments about this topic to Microsoft

Build date: 5/3/2011