Windows supports the following three data transfer mechanisms, also called I/O transfer types:
Buffered I/O operates on a copy of the user's data.
Direct I/O directly accesses the user's data through memory descriptor lists (MDLs) and kernel-mode pointers.
Neither buffered nor direct I/O-called "neither I/O" or METHOD_NEITHER-accesses the user's data through user-mode pointers.
WDF drivers can support any of the three I/O types. However, drivers should avoid the use of neither I/O because of inherent difficulties in properly validating and using user-mode pointers.
For device I/O control requests, the I/O control code itself includes the transfer type, so a device's IOCTLs can use any of the three transfer types and all of the IOCTLs are not required to use the same type. All read and write requests to a driver must use the same I/O transfer type because the transfer type for read and write requests is associated with the device object itself.
KMDF A KMDF driver must specify the I/O transfer type that each device object supports for read and write requests. To set the I/O transfer type, a KMDF driver calls WdfDeviceInitSetIoType in the EvtDriverDeviceAdd callback before creating the device object. A driver can specify one of the following WDF_DEVICE_IO_TYPE enumeration constants:
The default is WdfDeviceIoBuffered.
The Osrusbfx2 driver sets the I/O type to buffered I/O, as the following statement from the Device.c source file shows:
Calling WdfDeviceInitSetIoType from a filter driver has no effect. For filter drivers, the framework uses the I/O transfer type that the next-lower driver in the device stack specifies.
In KMDF, drivers can "opt in" to use direct or buffered I/O for read and write requests. Why doesn't the same option exist for UMDF drivers?
The reason is that all I/O in UMDF is buffered. The reflector copies request data from the caller-specified buffer or MDL to and from buffers in the host process. (Fortunately, modern CPUs are very efficient at copying data.) Therefore, there's little need for the driver to specify a desired transfer mode.
Because the reflector is the top driver on the kernel-mode device stack, it can use METHOD_NEITHER I/O for device I/O controls. The reflector carefully copies data directly between the application's original buffers and the host process.
I can hear you thinking, "That's fine for read and write, but you just told me I/O controls set their transfer mode in the control code. Aren't you doing extra copies there?" In Windows XP, an extra copy is performed for buffered and direct device I/O controls. In Windows Vista and later releases, the reflector directs the I/O manager to treat any IOCTL that it receives as METHOD_NEITHER regardless of the data transfer type, thus allowing us to avoid an extra copy on these newer system releases.
-Peter Wieland, Windows Driver Foundation Team, Microsoft
When the I/O manager sends a request for buffered I/O, the IRP contains an internal copy of the caller's buffer rather than the caller's buffer itself. The I/O manager copies data from the caller's buffer to the internal buffer during a write request or from the internal buffer to the caller's buffer when the driver completes a read request.
The WDF driver receives a WDF request object, which in turn contains an embedded WDF memory object. The memory object contains the address of the buffer on which the driver should operate.
When the I/O manager sends a request for direct I/O, the IRP contains the address of an MDL that describes the request buffer.
UMDF For a UMDF driver, the reflector validates the buffer length and access mode and copies this information into a buffer in the host process. The driver receives the new buffer in the WDF request object. The UMDF driver reads and writes this buffer just as it would any other buffer.
For a read or write request, the reflector copies data between the caller's buffer and the host process.
For device I/O control requests, the reflector proceeds as follows:
If the control code specifies METHOD_OUT_DIRECT, the reflector copies the contents of the input buffer to the host process.
If the control code specifies METHOD_IN_DIRECT, the reflector copies both the input and output buffers to the host process, because the output buffer can also serve as an additional input buffer. When a METHOD_IN_DIRECT request is complete, the reflector copies the contents of the output buffer from the host process back to the original IRP.
KMDF For a KMDF driver, the memory object that is embedded in the WDF request contains the address of an MDL that describes the request buffer, just as the IRP does. The MDL lists the buffer's virtual address and size, along with the physical pages in the buffer. The I/O manager locks these physical pages before issuing the IRP and unlocks them during IRP completion. KMDF drivers can use WDF methods to read and write the buffer or can read and write the buffer directly through the MDL. KMDF ensures that the driver receives a pointer to a system virtual address for the MDL, so that the driver is not required to map the address as a WDM driver must.
When the I/O manager sends a device I/O control request that specifies the METHOD_NEITHER transfer type, the IRP contains a pointer to the user-mode buffer that was supplied by the application that issued the request.
UMDF UMDF provides partial support for METHOD_NEITHER I/O through the UmdfMethodNeitherAction directive in the INF. This directive sets a value in the registry that the reflector reads to determine how to handle METHOD_NEITHER requests. By default, the reflector fails all requests for METHOD_NEITHER I/O.
If you enable METHOD_NEITHER I/O, the reflector copies the data and buffer lengths from the IRP to the host process. However, the reflector can successfully copy this information only if the user-specified addresses and buffer lengths are valid. In some I/O control requests, the length parameter does not actually specify a buffer length but instead supplies some other control code-specific data. If the design of the I/O control code makes it impossible for the reflector to determine whether a request is properly formed, you cannot assume that the data and buffer lengths that your UMDF driver receives are correct.
Furthermore, METHOD_NEITHER I/O control requests are a security risk. The I/O manager cannot validate the input and output buffer lengths, thus leaving the driver open to attack.
In general, you should enable METHOD_NEITHER I/O only if your driver requires it-and even then, only if the device I/O control requests that your driver supports use the parameters as they were intended.
To enable METHOD_NEITHER I/O for a user-mode driver, include the following directive in the driver's INF:
UmdfMethodNeitherAction = Action
where Action is either Copy or Reject.
Copy indicates that the WDF request object contains a copy of the data in the user's buffer.
Reject indicates that the reflector should fail the request.
The UmdfMethodNeitherAction directive is optional. The default is Reject.
KMDF A KMDF driver can receive METHOD_NEITHER requests from user-mode or kernel-mode callers.
If the request originates with a user-mode caller, the I/O manager passes a pointer to a user-mode buffer. Before accessing the WDF memory object, the driver must validate the address, lock the buffer into memory, and capture any data-such as buffer lengths and buffer pointers- that the driver requires to control the operation. A driver captures data by copying it to a safe kernel-mode location, where the caller cannot change it. This ensures that the driver uses valid data for its operations. KMDF drivers must perform the validation in an EvtIoInCallerContext callback. "Retrieving Buffers in KMDF Drivers" later in this chapter describes how to implement this callback.
If the request originates with a kernel-mode caller, no such requirements apply. The driver can directly access the embedded WDF memory object.