Guidelines for Adding Annotations to a Driver

Annotations provide detail about the intended use of a function and its parameters. They help define the contract between the caller of a function and the function that is called. Annotations help improve the accuracy of the analysis tools, by specifying how function parameters and return values are intended to be used. Annotations also can be used to specify the context in which certain functions must, or should not, be called.

You add annotations to the driver-supplied routines that you define in your driver code. The annotations must be placed on the function declarations and definitions. The system-supplied routines are already annotated.

Specify the Analysis Mode

Specify the analysis mode so that PFD can apply the appropriate rules.

For example, the following code example indicates that the code is a kernel-mode driver (which is also the default).

#include <wdm.h>
__drv_unit(kernel_driver);

Note   The __drv_unit(kernel_driver) annotation is functionally equivalent to the analysis macro __kernel_driver. For more information, see Specify the analysis mode. If the analysis mode is not specified, PFD assumes that it is __kernel_driver.

Declare Functions Using Function Role Type Declarations

For most driver types, you can declare functions using function role type declarations. The role types are implemented using function typedef declarations, so you benefit from the annotations that are applied to the role type when you declare a function of that type. Role types also allow PFD to apply your annotations to the function definitions that typically appear in your C and C++ source files. One benefit of using the role type declarations is if an annotation needs to be updated, you only need to update the role type declaration to have it apply to the function declaration and definition.

If you are writing a WDM driver, for example, you declare your driver's AddDevice function by using the annotation that declares the function role type. In this case, the function role type annotation is DRIVER_ADD_DEVICE. In the following example, the name of the driver-defined function is DriverAddDevice.

DRIVER_ADD_DEVICE DriverAddDevice; 

If you are writing a KDMF driver, for example, declare your driver's EvtDriverDeviceAdd event callback function by using the annotation that declares the function role type. In this case, the function role type annotation is EVT_WDF_DRIVER_DEVICE_ADD. In the following example declaration, the name of the driver-defined function is EvtDriverDeviceAdd.

EVT_WDF_DRIVER_DEVICE_ADD EvtDriverDeviceAdd;

Add the General Purpose Annotations to the Driver-Supplied Functions

Apply general purpose annotations to function parameters (for example, __in, __out, __opt).

Note   If you declare a function by using one of the defined function role type declarations or by using a function role type that you create with a typedef, you do not need to add any further annotations to the function. In these cases, the function inherits the annotations from the function role typedef.

In the following example, the __in annotations on the parameters DeviceObject and PhysicalDeviceObject indicate that the pointer arguments that are passed to the function are expected to be initialized prior to the function call. PREfast for Drivers (PFD) issues a warning if the function is called with a NULL pointer because the parameter was not declared optional (with an _opt modifier).

NTSTATUS
DriverAddDevice (
 __in PDRIVER_OBJECT  DriverObject,
 __in PDRIVER_OBJECT  PhysicalDeviceObject
    )
{
    return STATUS_SUCCESS;
}

The __out annotation indicates that the parameters are expected to be valid when the function returns. For example, the following annotations specify that the parameter pszDest is an output buffer that StringCcCopyA function initializes. The size of the buffer is expressed in number of elements (_ecount(size)) and the number of elements is specified by the input parameter cchDest. The annotations can be combined to make it easier to read (__out_ecount). For more information about __in, __out, and __ecount, see the Input and Output Parameter Annotations and Buffer Annotations topics.

.

StringCchCopyA(
 __out_ecount(cchDest) LPSTR pszDest,
 __in size_t cchDest,
 __in LPCSTR pszSrc);

Add Driver-Specific Annotations to the Driver-Supplied Functions

Apply driver specific annotations (for example, __drv_maxIRQL, __drv_raisesIRQL).

The following example shows a driver-supplied function, ResetDevice, that resets the device that is controlled by the driver. The annotation __drv_requiresIRQL specifies that the function must never be called when the IRQL is anything other than PASSIVE_LEVEL. The annotation directs PFD to issues a warning if the function is ever called when the IRQL is not at the level specified.

__drv_requiresIRQL(PASSIVE_LEVEL)
NTSTATUS
ResetDevice(
 __in WDFDEVICE Device
    );

The following example shows the annotation for the system-supplied function KeRaiseIrql. The __drv_maxIRQL(level) annotation specifies the maximum IRQL at which the function can be called. This annotation overrides the default IRQL (DISPATCH_LEVEL).

__drv_maxIRQL(HIGH_LEVEL)
VOID 
  KeRaiseIrql(
 __in __drv_in(__drv_raisesIRQL) KIRQL NewIrql,
    __out __drv_out_deref(__drv_savesIRQL) PKIRQL OldIrql
    );

The driver annotation __drv_in( ) specifies that the parameter NewIrql is a required input for the function. The __drv_in( ) annotation also contains the annotation, __drv_raisesIRQL, which further specifies that the NewIrql value can only raise the current IRQL. The driver annotations for the OldIrql parameter ( __drv_out_deref(__drv_savesIRQL)), specify the conditions that apply to the parameter after the function call. Namely, that the KeRaiseIrql function saves a pointer to the old IRQL, and that the pointer must not be NULL.

For more information about the placement of annotations, see PREfast for Drivers Annotation Syntax.

Test the Annotations

Test your annotations. Start with simple test cases using the basic general purpose annotations (for example, __in, __out) and then extend to include driver-specific annotations (for example, __drv_requiresIRQL). Run PFD on your code and refine the annotations that are based upon the results that PFD reports. Use the annotations to minimize the noise and to help PFD understand the intentions of your code. The annotations can help you pinpoint the warnings that matter most.

For more information about adding annotations to your driver code and the design decision you should consider, see Best Practices for Using Annotations.

 

 

Send comments about this topic to Microsoft

Build date: 5/3/2011