Memory Annotations
Annotations can help PREfast for Drivers (PFD) to more accurately detect leaks and other problems with allocations of memory.
Drivers often use special-purpose functions to allocate and free memory. Drivers also often pass memory out of a function in some way that causes the memory to be aliased, that is, where the called function saves the value of the parameter in some location where it will be found, and presumably freed, later.
Use the following annotations to help PFD more accurately check functions that allocate memory:
__drv_allocatesMem(type)
__drv_freesMem(type)
__drv_aliasesMem
Allocating and Freeing Memory
Use the __drv_allocatesMem(type) annotation to specify that the output value is allocated, either through a parameter or through the function result. This annotation should be placed on the object where the memory is located. For example, ExAllocatePoolWithTag returns a pointer to the memory allocated, so the return type has the __drv_allocatesMem annotation. The IoCreateDevice function allocates memory for a DEVICE_OBJECT, so the DeviceObject parameter takes __drv_allocatesMem annotation. The IoRegisterDeviceInterface function allocates a Unicode string structure that the SymbolicLinkName points to, so the __drv_at annotation is used to to apply the __drv_allocatesMem(Mem) annotation on SymbolicLinkName->Buffer . This particular annotation is shown in the Memory Annotation Examples that follow.
The type parameter indicates the type of memory allocator that is used:
For malloc and free, type should be mem.
For operator new, type should be object.
Note At this time, the type parameter is advisory; PFD does not check it. However, this parameter might be checked by a future version of PFD.
You should also add the __checkReturn annotation to a function that allocates memory if the function indicates failure by returning NULL.
Use the __drv_freesMem(type) annotation to specify that the memory that is passed as an input parameter is freed. After the function returns, the annotated parameter is assumed to be in an uninitialized state and, until the parameter is changed, further access through the actual parameter is treated as an access to an uninitialized variable. The type should match the type that is used with the __drv_allocatesMem annotation.
Aliasing Memory
Add the __drv_aliasesMem annotation to input parameters (including __in and __out parameters) to indicate that the called function saves the value of the parameter in some location where it will be found, and presumably freed, later.
In general, PFD cannot confirm whether memory that is aliased is actually freed. The memory can continue to be accessed after a call to a function with this annotation. PFD tries to identify when memory is aliased in several different ways, but it cannot determine whether memory is aliased for called functions without this annotation.
The __drv_aliasesMem annotation helps to suppress false warnings about possible leaking memory by PFD. It does not take a type parameter because it operates in the same way on all kinds of memory.
Some functions return a pointer to memory that is already aliased. That is, the caller of the function is simply given access to a block of memory that will survive the caller's return. It is also not uncommon to assign some newly allocated memory into the already aliased object. If that is done, to tell PFD that this is long-lived memory, the value returned can be annotated with __drv_aliasesMem.
Note The __drv_freesMem and __drv_aliasesMem annotations are mutually exclusive. __drv_freesMem indicates that the memory is discarded (that is, the memory is no longer accessible), and PFD enforces that it is no longer accessible by invalidating the variable that contains the pointer to the freed memory. The __drv_aliasesMem indicates simply that there is no longer a risk of that memory leaking, but the memory does continue to exist and can be subsequently accessed.
Memory Annotation Examples
Functions that allocate and free memory. The ExAllocatePool and ExFreePool functions are the classic examples of functions that allocate and free memory. The following example shows some of the annotations that are applied to these functions. (These functions have additional parameter checks that are not shown here.) The IoAllocateDriverObjectExtension and IoRegisterDeviceInterface functions are examples that show memory annotations on parameters.
__drv_allocatesMem(Mem)
__drv_when(((PoolType&0x1))!=0, __drv_maxIRQL(APC_LEVEL))
__drv_when(((PoolType&0x1))==0, __drv_maxIRQL(DISPATCH_LEVEL))
__drv_when(((PoolType&0x2))!=0,
__drv_reportError("Must succeed pool allocations are forbidden. "
"Allocation failures cause a system crash"))
__drv_when(((PoolType&(0x2|POOL_RAISE_IF_ALLOCATION_FAILURE)))==0,
__post __maybenull __checkReturn)
__drv_when(((PoolType&(0x2|POOL_RAISE_IF_ALLOCATION_FAILURE)))!=0,
__post __notnull)
__bcount(NumberOfBytes)
NTKERNELAPI
PVOID
NTAPI
ExAllocatePoolWithTag(
__in __drv_strictTypeMatch(__drv_typeExpr) POOL_TYPE PoolType,
__in SIZE_T NumberOfBytes,
__in ULONG Tag
);
The following example shows the annotations for the memory allocation in a parameter:
__drv_maxIRQL(DISPATCH_LEVEL)
__drv_valueIs(<0;==0)
NTKERNELAPI
NTSTATUS
IoAllocateDriverObjectExtension(
__in PDRIVER_OBJECT DriverObject,
__in PVOID ClientIdentificationAddress,
__in ULONG DriverObjectExtensionSize,
// When successful, this always allocates already-aliased memory.
__post __deref __drv_when(return==0,
__out __drv_aliasesMem __drv_allocatesMem(Mem) __drv_valueIs(!=0))
PVOID *DriverObjectExtension
);
The following shows the annotations for a memory allocation in an object contained within a structure:
__drv_maxIRQL(PASSIVE_LEVEL)
__checkReturn
NTKERNELAPI
NTSTATUS
NTAPI
IoRegisterDeviceInterface(
__in PDEVICE_OBJECT PhysicalDeviceObject,
__in CONST GUID *InterfaceClassGuid,
__in_opt PUNICODE_STRING ReferenceString,
__out __drv_when(return==0,
__drv_at(SymbolicLinkName->Buffer, __drv_allocatesMem(Mem)))
PUNICODE_STRING SymbolicLinkName
);
NTKERNELAPI
VOID
ExFreePoolWithTag(
__in __drv_in(__drv_freesMem(Pool))
PVOID P,
__in ULONG Tag
);
A function that aliases memory. The InsertHeadList function takes and holds on to Entry. That is, it aliases the memory that is occupied by Entry. This annotation suppresses a potential memory leak warning from PFD, but leaves Entry accessible.
VOID
InsertHeadList(
__in PLIST_ENTRY ListHead,
__in __drv_in(__drv_aliasesMem) PLIST_ENTRY Entry
);
Allocating and aliasing example
The IoCreateDevice function allocates memory and returns it in an __out parameter. That memory must then be dealt with by reaching IoAttachDeviceToDeviceStack (where it is aliased) or IoDeleteDevice (usually called on some sort of failure).
__drv_maxIRQL(APC_LEVEL)
__drv_valueIs(==0;<0)
NTKERNELAPI
NTSTATUS
IoCreateDevice(
__in PDRIVER_OBJECT DriverObject,
__in ULONG DeviceExtensionSize,
__in_opt PUNICODE_STRING DeviceName,
__in DEVICE_TYPE DeviceType,
__in ULONG DeviceCharacteristics,
__in BOOLEAN Exclusive,
__out
__drv_out_deref(
__drv_allocatesMem(Mem)
__drv_when((((inFunctionClass$("DRIVER_INITIALIZE"))
||(inFunctionClass$("DRIVER_DISPATCH")))),
__drv_aliasesMem)
__on_failure(__null))
PDEVICE_OBJECT *DeviceObject
);
TheIoAttachDeviceToDeviceStack function can fail. If it does, the memory can still leak so IoDeleteDevice must be called. In the case of IoAttachDeviceToDeviceStack, the memory can continue to be accessed.
PDEVICE_OBJECT
__checkReturn
IoAttachDeviceToDeviceStack(
__in PDEVICE_OBJECT SourceDevice,
__in
__drv_in(__drv_mustHold(Memory)
__drv_when(return!=0, __drv_aliasesMem))
PDEVICE_OBJECT TargetDevice
);
If a failure occurs, either because of IoAttachDeviceToDeviceStack or some other failure, the device must be deleted. After IoDeleteDevice is called, PFD reports any attempt to access the device object as an error.
NTKERNELAPI
VOID
IoDeleteDevice(
__in __drv_freesMem(Memory)
PDEVICE_OBJECT DeviceObject
);
Send comments about this topic to Microsoft
Build date: 5/3/2011