MemCheck Driver Memory Tool

Environment: Windows NT/2K/XP driver development

Purpose


The MemCheck code is designed to provide Windows NT/2K/XP driver developers with a tool to help in the detection of the following memory handling issues:

Buffer overrun
Buffer corruption
Buffer use after buffer release
Double buffer releases


MemCheck’s Routines


MemCheck has a specific API (set of functions) that should be used in place of existing routines.

Original routine
MemCheck routine
ExAllocatePool(…)
MEM_ALLOCATE(…)
ExAllocatePoolWithTag(…)
MEM_ALLOCATE_WITH_TAG(…)
ExFreePool(…)
MEMCHECK_FREE(…)

The parameters for each of the MemCheck routines are exactly the same as those in the original (DDK) functions. By replacing the original calls with the memCheck versions, validation can be done during the allocation and release routines.

In addition to the functions listed above, MemCheck also has the following routines:

MemCheck Routine Description
MEM_CHECK_INIT() Initialize memCheck's resources. This routine should be called before any other memCheck routines are used.
MEM_CHECK() Forces memCheck to validate all buffers being monitored.
MEM_LIST_DISPLAY()
Displays a list of all buffers being monitored by memCheck. In addition, each buffer being monitored is validated for correctness (the header and footer tags are checked).
MEM_VALID_ACCESS() Tests ptr to see if it points to a location within a valid buffer being monitored by memCheck.
MEM_CHECK_EXIT() Frees resources used by memCheck. This routine should be called when unloading. Make sure not to make further calls to memCheck routines after calling MEM_CHECK_EXIT()


MemCheck Settings


The settings for MemCheck are contained in the memcfg.h file. Each of these settings is a simple macro. By defining and undefining a macro, the setting can be turned on or off.

MemCheck Setting

Description
MEMCHECK_ON Enable/disable the entire memcheck package.
MEMCHECK_DEBUGPRINT
Enables displaying of MEMCHECK_DEBUG() macro information this is mostly debug information for memCheck itself.
MEMCHECK_DISPLAYPRINT
Enables displaying of MEMCHECK_PRINT() macro information
MEMCHECK_DISPLAY_FREES
Display information when buffer is freed
MEMCHECK_DOUBLE_FREES Enable checking for double frees...this causes extra memory to be used and should NOT be left enabled for a shipping product.
MEMCHECK_HALT_ON_BAD Causes a debug breakpoint to occur when a bad buffer tag is encounted
MEMCHECK_FREE_ON_EXIT Allows memcheck to free its resources upon exit. This is normally left on



How MemCheck works


MemCheck’s main functionality is exercised when all allocation and release routines (ExAllocatePool(…), ExAllocatePoolWithTag(…) and ExFreePool(…)) are replaced with the MemCheck analog (see MemCheck’s Routines above for more information). With these routines in place, MemCheck maintains a list of each buffer allocated. When the client requests a 1K buffer be allocated, MemCheck actually allocates a larger buffer. This extra space is used to store tags at the start (header) and end (footer) of the buffer. The client is returned a pointer to a buffer of the requested size (assuming the call was successful). MemCheck then writes a specific value in the header and footer tags. If at any point MemCheck realizes that the tags are invalid, it can tell that the user has perturbed their memory.

NOTE: MemCheck stores more information within the buffer than just the tags and client data. The above description is a simplification of the actual method MemCheck uses. Please refer to memcheck.c code to see how the real buffer list is maintained.

Another useful feature of MemCheck is MEM_VALID_ACCESS(). By calling MEM_VALID_ACCESS(ptr), MemCheck can compare the ptr address to the addresses of each of the monitored buffers. If the address is not within a valid buffer range, MemCheck can display a warning.

When the MEMCHECK_DOUBLE_FREES macro is defined (in memcfg.h) and the client frees a buffer, the buffer is not actually released. Instead, it’s information is maintained. In the event that an attempt is made to free the same ptr, MemCheck will be able to notify the client.


Using the MemCheck Code


The first step in using the MemCheck code is to call MEM_CHECK_INIT() when your driver initializes and MEM_CHECK_EXIT() when your driver unloads. The next step is to replace all the original DDK memory allocation and release calls with the MemCheck counterparts (see MemCheck’s Routines above for more information). After completing these steps, make sure MemCheck is enabled (via the MEMCHECK_ON macro in memcfg.h). Then build the driver and run it. MemCheck will be able to scour the memory usage right away and try to find problems.

In addition to the previous steps, users may find it useful to use the other ‘helper’ functions to track down buffer problems. These functions are

  • MEM_CHECK()
  • MEM_LIST_DISPLAY()
  • MEM_VALID_ACCESS()

(see MemCheck Routines above for a description of these routines).

When you have finally tracked down all memory issues, you can disable the MemCheck package by undefining the MEMCHECK_ON macro in memcfg.h. This has the effect of making your code build with no code from MemCheck being included. This means that the size and performance of the driver will be the same as one using only the DDK calls.

Downloads

Download source - 28.8 Kb


Comments

Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

  • Hurricane Sandy was one of the most destructive natural disasters that the United States has ever experienced. Read this success story to learn how Datto protected its partners and their customers with proactive business continuity planning, heroic employee efforts, and the right mix of technology and support. With storm surges over 12 feet, winds that exceeded 90 mph, and a diameter spanning more than 900 miles, Sandy resulted in power outages to approximately 7.5 million people, and caused an estimated $50 …

  • Ever-increasing workloads and the challenge of containing costs leave companies conflicted by the need for increased processing capacity while limiting physical expansion. Migration to HP's new generation of increased-density rack-and-blade servers can address growing demands for compute capacity while reducing costly sprawl. Sponsored by: HP and Intel® Xeon® processors Intel, the Intel logo, and Xeon Inside are trademarks of Intel Corporation in the U.S. and/or other countries. HP is the sponsor …

Most Popular Programming Stories

More for Developers

Latest Developer Headlines

RSS Feeds