Hello, list:
I am writing a file system “proxy” driver for Windows. The driver will be used to proxy file system calls and forward them to a user-space application for processing. The model is similar to the one from FUSE in Linux/OSX land or Dokan (CFBS, etc.) in Windows.
I am currently implementing IRP_MJ_WRITE/IRP_MJ_READ. One “obvious” optimization when transferring large buffers using these IRP’s is to map the client process (i.e. the one doing the ReadFile/WriteFile) I/O buffer directly into the user-mode file system process; this will avoid unnecessary buffer allocation and copying. I am planning to do this using MmMapLockedPagesSpecifyCache() (and MmUnmapLockedPages upon IRP completion) in the user-mode file system process context.
One potential gotcha is that the buffers passed to ReadFile/WriteFile are not necessarily page aligned. [In the case of non-buffered I/O they are sector-aligned but not page aligned.] If I map directly the client process I/O buffer into the user-mode file system process, the user-mode file system will be able to read and/or modify content outside the bounds of the I/O buffer. For example, if a buffer starts at 0x10000200 it would be sector aligned, but the user-mode file system would be able to read/write memory contents from 0x10000000 to 0x10000200.
A solution to this problem is to treat the start and end of an I/O buffer specially when the buffer is not page aligned. Suppose that I receive an MDL with such a buffer. I would like to convert it to an MDL that is “safe”, in that it does not allow the user-mode file system to poke around where it does not belong. My technique for achieving this is as follows:
-
Ensure that the source MDL has been probed/locked.
-
IoAllocateMdl a target MDL with the same page count as the source MDL and RtlCopyMemory the PFN array from the source MDL to the target MDL.
-
Allocate from 0 to 2 pages of non-paged pool using ExAllocatePoolWithTag(NonPagedPool, …). Initialize them to zero, then copy the correct portion(s) of the I/O buffer onto the pool pages.
-
IoAllocateMdl a temporary MDL array to describe the non-paged pool and then call MmBuildMdlForNonPagedPool on it.
-
If the start of the I/O buffer is not page aligned set the target MDL PFN array 0-th element to the value from the temporary MDL. Likewise for the end of the I/O buffer (and setting the last element of the target PFN array).
-
Free the temporary MDL array.
-
MmMapLockedPagesSpecifyCache the target MDL array in the user-mode file system process.
The target MDL is “safe” because regardless of whether the user-mode file system process reads/writes outside of the bounds of the passed I/O buffer it cannot harm the client process.
Which brings me to my question. Will this technique work and create an MDL that will work for my purposes? I am particularly weary of mixing the PFN arrays from the source and temporary MDL’s.
Bill Zissimopoulos
PS: An alternative to this scheme may be to set the DeviceObject’s SectorSize to always be a multiple of PAGE_SIZE. But I would rather not force the SectorSize like that unless I really have to.