Creating Reliable Kernel-Mode Drivers
Drivers make up a significant percentage of the total code that executes in kernel mode. A kernel-mode driver is, in effect, a component of the operating system. Therefore, drivers that are reliable and secure contribute significantly to the overall trustworthiness of the operating system. To create a reliable kernel-mode driver, follow these guidelines:
Secure device objects properly.
User access to a system's drivers and devices is controlled by security descriptors that the system assigns to device objects. Most often, the system sets device security parameters when a device is installed. For more information, see Creating Secure Device Installations. Sometimes it is appropriate for a driver to play a part in controlling access to its device. For more information, see Securing Device Objects.
Validate device objects properly.
If a driver creates multiple types of device objects, it must check which type it receives in each IRP. For more information, see Failure to Validate Device Objects.
Use "safe string" functions.
When manipulating strings, a driver should use safe string functions instead of the string functions that are supplied with C/C++ language runtime libraries. For more information, see Using Safe String Functions.
Validate object handles.
Drivers that receive object handles as input must verify that the handles are valid, are accessible, and are of the type expected. For more information about using object handles, see the following topics:
Support multiprocessors properly.
Never assume that your driver will run only on single-processor systems. For information about programming techniques that you can use to ensure that your driver will function properly on multiprocessor systems, see the following topics:
Handle driver state properly.
It is important to always verify that your driver is in the state you assume it to be in. For example, if the driver receives an IRP, is it already servicing an IRP of the same type? If the driver does not check for this situation, the first IRP could be lost. For more information, see Failure to Check a Driver's State.
Validate IRP input values.
It is essential, from both a reliability and a security perspective, to validate all values that are associated with an IRP, such as buffer addresses and lengths. The following topics provide information about validating IRP input values:
DispatchReadWrite Using Buffered I/O
DispatchReadWrite Using Direct I/O
Handle the I/O stack properly.
When passing IRPs down the driver stack, it is important for drivers to call IoSkipCurrentIrpStackLocation or IoCopyCurrentIrpStackLocationToNext to set up the next driver's I/O stack location. Do not write code that directly copies one I/O stack location to the next.
Handle IRP completion operations properly.
A driver must never complete an IRP with a status value of STATUS_SUCCESS unless it actually supports and processes the IRP. For information about the correct ways to handle IRP completion operations, see Completing IRPs.
Handle IRP cancellation operations properly.
Cancel operations can be difficult to code properly because they typically execute asynchronously. Problems in the code that handles cancel operations can go unnoticed for a long time, because this code is typically not executed frequently in a running system.
Be sure to read and understand all of the information supplied under Canceling IRPs. Pay special attention to Synchronizing IRP Cancellation and Points to Consider When Canceling IRPs.
One way to avoid the synchronization problems that are associated with cancel operations is to implement a cancel-safe IRP queue. A cancel-safe IRP queue is a driver-managed queue that was introduced for Windows XP and later operating system versions, but is also backward-compatible to earlier versions.
Handle IRP cleanup and close operations properly.
Be sure that you understand the difference between IRP_MJ_CLEANUP and IRP_MJ_CLOSE requests. Cleanup requests arrive after an application closes all handles on a file object, but sometimes before all I/O requests have completed. Close requests arrive after all I/O requests for the file object have been completed or canceled. For more information, see the following topics:
DispatchCreate, DispatchClose, and DispatchCreateClose Routines
For more information about handling IRPs correctly, see Additional Errors in Handling IRPs.
Using Driver Verifier
Driver Verifier is the most important tool you can use to ensure the reliability of your driver. Driver Verifier can check for a variety of common driver problems, including some of those discussed in this section. However, use of Driver Verifier does not replace careful, thoughtful software design.