Testing for Errors in Accessing and Allocating Memory
Tips for finding errors in memory access and allocation in kernel-mode drivers.
- Use Tags When Allocating Memory
- Use Driver Verifier
- Set Global Flags
- Monitor Tagged Memory Allocations with PoolMon
- Combining Techniques
- Related topics
Drivers often have errors related to memory access and allocation. Such errors can lead to memory corruption and, eventually, system crashes. The most common errors involve:
- Attempts to access memory that has already been freed
- Attempts to access beyond the end of allocated memory (memory overruns)
- Attempts to access before the start of allocated memory (memory underruns)
You should test every driver for such errors before releasing it—even a driver that seem to be working properly. Crashes caused by memory errors can occur long after a driver accesses an invalid location. Corrupted memory can cause errors and crashes in processes that are completely unrelated to the offending driver.
The tools and techniques described here can help you uncover problems that might otherwise go undetected.
Use ExAllocatePoolWithTag, ExAllocatePoolWithQuotaTag, and ExAllocatePoolWithTagPriority to allocate memory in your driver. These routines enable you to assign a one- to four-character tag (a pool tag) to each memory allocation. Make sure that you use unique tags that distinguish your driver from all other drivers in the system. If your driver is large, considering assigning a different tag for each class of memory allocation. Smaller drivers commonly use only a single tag for all allocations.
Driver Verifier, GFlags, and PoolMon use these tags in tracking pool allocations. In addition, the debuggers display the tags along with the contents of a buffer.
The best way to find problems related to memory usage is to run Driver Verifier. Driver Verifier has four options designed to detect such problems:
- Special memory pool
- Memory pool tracking
- DMA checking
- Low resources simulation
When you enable the special memory pool option for a driver, Windows attempts to satisfy all of the driver's memory allocation requests from the special memory pool. The size of the special pool is limited, depending on the amount of physical memory present on the test system. When the special memory pool becomes depleted, Windows allocates memory from the normal pool.
When you enable the memory pool tracking option, Driver Verifier:
- Monitors memory allocations made by the driver.
- Checks whether the driver frees all the memory that it allocated.
- Checks for attempts to write to memory outside of the driver's allocations.
You should use the special memory pool and memory pool tracking options to test every driver.
Memory pool tracking is not used, however, for graphics drivers. Direct memory access (DMA) checking is available on Windows XP and later versions of Windows. When this option is enabled, Driver Verifier checks for a variety of errors related to DMA, such as buffer overruns, errors in buffer allocations, and errors in address mappings. You should use this option on every driver that uses DMA directly, and on all miniport drivers, which might use DMA indirectly through calls to port drivers that use DMA. Like memory pool tracking, DMA checking is not used for graphics drivers.
Driver Verifier issues a bug check if the driver fails any of the special memory pool, memory pool tracking, and DMA checks.
Driver Verifier can also simulate low resources, thus helping you detect errors that might occur when the driver is unable to allocate all the memory it requests. When this option is enabled, Driver Verifier causes memory allocation requests to fail randomly, starting approximately seven minutes after system startup.
On Windows XP and later versions of Windows, the special memory pool, memory pool tracking, and DMA verification options are all part of the standard options in Driver Verifier. That is, they are enabled if you start Driver Verifier from the command line with the /standard parameter or choose "Create standard settings" in the first Driver Verifier Manager dialog box. To enable low resource simulation, you must choose Create Custom Settings in the dialog box or use the /flags 0x4 parameter on the command line.
The Global Flags utility (GFlags.exe) controls two features that are useful in testing for memory usage problems:
- Pool tagging option
- Kernel special pool tag options
When you enable the pool tagging option, Windows collects data and calculates statistics about pool memory allocations. You can display this data using PoolMon or PoolTag.
The kernel special pool tag options control whether Driver Verifier performs overrun or underrun detection on memory that has been allocated from the special pool. In addition, you can use these options to request that all memory allocations with a specified tag or size range come from the special pool.
The easiest way to enable GFlags options is to start GFlags from the command line and then use the dialog box as follows:
- To enable pool tagging, select "Enable pool tagging." (On Microsoft Windows Server 2003, this option is permanently enabled.)
- To enable overrun detection, select "Verify End" in the Kernel Special Pool Tag box.
- To enable underrun detection, select "Verify Start" in the Kernel Special Pool Tag area.
- To allocate memory with a particular tag or size from the kernel special pool, enter the tag or size range in the text box. (For details about how to specify the size range, see the Windows DDK.)
The options do not take effect until you restart the target system.
GFlags and Driver Verifier overlap in their use of the special memory pool. If you use both tools to set special memory pool options, Windows will try to use the special pool for any allocation request that matches the specified pool tag or size or is from the driver being verified.
If your driver does not allocate large amounts of memory, using Driver Verifier to allocate all requests from special pool is probably adequate; setting allocations based on a specific tag or size is probably unnecessary. However, if you have a large driver that requests a lot of memory, consider using several pool tags in the driver and activating them one at a time with GFlags. Limiting the allocations from the special pool in this way can help to prevent the pool's depletion, thus ensuring that you test all memory allocations.
PoolMon displays data about tagged memory allocations. PoolMon runs in a command window. You can run PoolMon for long periods of time while your driver operates—even as it runs with Driver Verifier—to display data about memory allocations. This data can help you find memory leaks. If you suspect problems with a particular memory allocation, you can use this tool to monitor only that allocation.
The best way to test for possible memory access problems is to combine these techniques. For example:
- Run GFlags to enable pool tagging, to select overrun or underrun detection, and, if necessary, to specify tags for memory allocations from the kernel special pool.
- Enable Driver Verifier for your driver on the target system.
- Reboot the target system.
- Run the driver with a debugger attached. To get information from Driver Verifier within the debugger, use the !verifier command (or, for a graphics driver, the !gdikdx.verifier command).
- Start PoolMon on the target computer, specifying the types of allocations (paged or nonpaged) and the tags your driver assigns.
On Windows XP and Windows Server 2003, you should perform at least two test cycles. During the first cycle, perform the steps in the preceding list. During the second cycle, disable the special memory pool option in Driver Verifier, and then run PoolMon to check for memory leaks in tagged allocations.