Bugs related to memory access and allocation
are common, but they can be difficult to find. Symptoms can range from a
gradual slowing of system performance because of memory leaks to an abrupt
crash if a driver attempts to access paged memory at the wrong IRQL. Crashes
caused by such bugs 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.
Some of the most common memory-related errors
involve the following:
·
Attempts to access memory that
has already been freed.
·
Attempts to access beyond the
end of allocated memory (memory and buffer overruns).
·
Attempts to access before the
start of allocated memory (memory and buffer underruns).
·
Attempts to access paged memory
at IRQL DISPATCH_LEVEL or higher.
·
Failure to free memory (memory
leaks).
·
Freeing the same memory twice.
·
Failure to check the status
after attempting to allocate or reference memory.
Tools for Discovering Memory-Related Errors
Many of the tools provided with the
Windows DDK offer features specifically designed to find memory-related errors.
For example:
·
Driver Verifier
Driver Verifier has options to use the kernel special memory pool, track memory
pool usage, check DMA operations, and simulate low-resource situations.
·
Global Flags (GFlags)
GFlags sets flags that enable pool tagging, provide for buffer overrun and
underrun detection, and specify tags for memory allocations from the kernel
special pool.
·
Call Usage Verifier (CUV)
CUV checks to ensure that data structures that must be in nonpaged memory are
actually there.
·
PoolMon and PoolTag
PoolMon and PoolTag gather and display data about memory allocations. Tracking
this data can help you to find memory leaks.
·
PREfast with driver-specific rules
PREfast can detect potential
errors in source code by simulating execution of all possible code paths on a
function-by-function basis. Errors detected by PREfast include memory and resource leaks, excessive stack use, and
incorrect usage of numerous kernel-mode functions.
The best way to test your driver is to
use all of these tools repeatedly, both independently and in combination. Using
CUV with Driver Verifier can be particularly useful.
You should also test on various memory
configurations. To change the memory configuration of the test system, you can
use various boot parameters.
The /3GB switch expands user space to 3
GB, leaving only 1 GB for all kernel-mode components.
On x64 and Itanium systems, and on x86
systems that have PAE enabled and more than 5 GB of physical memory, the /nolowmem
switch loads the system, drivers, and applications into memory above the 4‑GB
boundary. Testing under these conditions helps you check for errors that are related
to address truncation when using memory above 4 GB and inappropriate reliance
on memory below 4 GB.
The /maxmem and /burnmemory switches also
limit the memory available to the system by reducing the available memory by a
specific amount. The /maxmem switch can be used with Windows 2000 or later
versions of Windows. The /burnmemory switch is more precise and can be used
with Windows XP and later versions.
Best Practices for Programming and Testing
Good programming and testing techniques
can help you prevent problems as well as find them. The following should be
standard practices for all driver writers:
·
Always use pool tags when
allocating memory. Driver Verifier, GFlags, PoolMon, and PoolTag use these tags
in tracking pool allocations, and the debuggers display a buffer’s tag along
with its contents.
·
Validate the length of every
buffer before accessing it.
·
Validate every user-space
address by probing within an exception handler.
·
Check the returned status every
time you try to allocate or access memory. Never assume that an operation cannot
fail. If an error occurs, return a failure status from the driver or modify and
retry the operation if appropriate. Remember: if an attempt to access memory
fails because of an invalid pointer and your driver fails to catch the error
when it occurs, the driver could propagate the addressing error, resulting in
errors that appear much later and seem unrelated to the driver.
·
Always use Driver Verifier.
·
Test every driver on both
single-processor and multiprocessor hardware. Because Windows treats
hyper-threaded processors as two CPUs, you can perform minimal multiprocessor
testing on a machine with a single hyper-threaded processor. In effect, a
dual-processor hyper-threaded machine provides four processors and serves as
better test system. The new multicore processors would be an even better test
system than the hyper-threaded systems.
For additional suggestions for testing,
see “Testing for Errors in Accessing and Allocating Memory”, which is listed in
the Resources section at the end of this paper.
0 komentar:
Posting Komentar