Setting up device objects for neither direct nor buffered I/O causes the I/O Manager to pass the original user-space virtual addresses in IRPs sent to the driver. Because such a driver must be executing in the context of the calling thread to access its buffer(s) safely, only highest-level NT drivers, such as FSDs, can set up their device objects without setting a bit in each device object’s Flags by ORing it with DO_DIRECT_IO or DO_BUFFERED_IO.
Note that an NT intermediate or lowest-level driver cannot always meet this condition. For example, if a requesting thread waits on the completion of an I/O request or if a higher-level driver (particularly a file system) is layered over the intermediate or lowest-level driver, such a lower-level driver’s routines are unlikely to be called in the context of the requesting thread.
When the I/O Manager sends an IRP with the current thread’s user-space virtual addresses for a buffer, a driver that did not OR its device object(s)’ Flags with DO_BUFFERED_IO or DO_DIRECT_IO must do the following:
In effect, such a driver must choose on a per-IRP basis whether to do buffered I/O, direct I/O, or I/O in the context of the calling thread, and it must handle any exceptions that might occur in a user-mode thread context. Such a driver must manage its own user buffer accesses, double-buffering operations, and memory mappings, as necessary, instead of letting the I/O Manager handle these operations for the driver.