Finding Dummy page PFN_NUMBER/PHYSICAL_ADDRESS used by system provided MDLs

How can I find the PFN_NUMBER or PHYSICAL_ADDRESS of the Dummy page used to modify system MDLs?
I have a filter driver that manipulates the data in the read/write buffers.
Unfortunately under Vista/Win7 the system uses a system-wide dummy page to replace valid user-mode pages that are not modified/read. Here is an article about it:
http://msdn.microsoft.com/en-us/windows/hardware/gg463193
The suggestion is to use a secondary buffer and copy the data between the two. Unfortunately this causes a huge performance hit which I would like to avoid.

  1. Is there a way to figure out what page the system uses as its dummy page so I don’t have to double buffer if the DUMMY page is not present?
  2. the MS documentation states that this is a single page. Is that true? I have noticed that the page (PFN) changes when the system reboots but I have no proof that only one page is used as a DUMMY page at any time.
    I do have two methods for figuring out when a dummy page is used but I need something faster:
    A. (while in the caller’s thread context) create a duplicate MDL analogous to the system supplied MDL using the user mode virtual address. Lock both MDLs (MmProbeAndLockPages). Since they are describing the same virtual address space they should be using the same physical pages. If you compare the PFNs they should be the same. If they are not, than the system used a dummy page.
    B. walk the MDL (or the system buffer) and compare the physical pages describing the range to each other. If a more than one page is mapped to the dummy page, this can find it.
    C. use the PFN discovered by ?A? and or ?B?
    Each of those methods have its pros and cons.
  • ?A? works only if you are in the caller?s thread context. It also pokes inside the MDL’s PFN array which is frowned upon.
  • ?B? is fast and works in any context (as long as the MDL pages are locked) but can miss MDLs with a single dummy page.
  • ?C? assumes that there is only one ?System-wide? dummy page and relies on the output from ?A? and/or ?B?.
    Any suggestions?

If the user app does write-type operation, you’re NOT supposed to modify the buffer EVER. Even if it’s not a dummy page. Just bite the bulet and do intermediate buffering.

> Here is an article about it: http://msdn.microsoft.com/en-us/windows/hardware/gg463193

Probably I just misunderstood something, but the whole things seems really weird to me - it seems to be just
full of erroneous statements.

Let’s look at this article in more detail:

Drivers, indeed, “should not directly dereference a memory location to get the data”, but for a totally different reason - if the target buffer resides in the userland its virtual address may simply be invalid in context of a thread that accesses it. Therefore, assuming that MDL has already been locked, driver has to map it to the kernel address space by calling MmMapLockedPagesSpecifyCache() before it can be safely accessed.
In any case, it has nothing to do with app and driver trying to modify data at the same time. After all, if an app does direct IO on a buffer, it is app’s responsibility to ensure that it does not accesses the target buffer before IO operation is complete…

Never mind - the more interesting stuff is on the way…

This part seems to be just logically inconsistent. Indeed, MM may want to read data into a dummy page X
when it locks down pages for MDL if pages Z and Y are already resident, but what on Earth does it have to do with MDL as it is seen by a driver??? When it comes to filling the array with PFNs it has to put pages Z and Y, rather than X, into it anyway - otherwise we are going to end up in a situation when physical pages that actually represent the buffer that MDL describes are different from the ones specified in MDL.

Anton Bassov

Anton,

This looks like a mix of generic I/O related and paging I/O related stuff. I’m not sure what it’s trying to say.

A dummy page can never be filled with garbage, otherwise it becomes an information disclosure risc.

If an I/O operation is a read to a buffer with a number of discarded pages, the MM may decide to not bring those pages back because they’ll be overwritten anyway. BUT when those pages are locked for read op (for modification) the MM has to map distinct zero-initialized physical pages and lock them. And this is done in the usermode client context. The usermode process will see those pages.

For IoWriteAccess, the freshly committed unmodified pages MAY be mapped from a dummy zero page. This is the only case the locked MDL will see a dummy page. But THE DRIVER IS NOT ALLOWED TO MODIFY THE WRITE BUFFER ANYWAY.

Yes this article is a bit confusing. The problem with concurrent access from the app and the driver (e.g. during a WriteFile operation) is that even if the driver accesses MDL contents through a system mapping, the data it’s reading can be concurrently modified by the app. This would of course mean a buggy (or malicious) app, but the driver still has to make sure it doesn’t crash or cause other damage as a result. For drivers that perform non-trivial data processing this may be harder than it looks.

In the paging read case, Mm can put dummy pages into the MDL so it can issue one large read request instead of several smaller ones. These dummy pages will in fact contain garbage (e.g. data from other reads happening in parallel) but that’s OK because this garbage is never exposed to user apps. Drivers on the other hand must deal with the possibility that the contents of any page in the MDL might change asynchronously.

Currently there is no supported way to determine whether a given MDL contains dummy pages or not, so double-buffering is the only option for drivers that require MDL data to be stable.

-----Original Message-----
From: xxxxx@lists.osr.com [mailto:xxxxx@lists.osr.com] On Behalf Of xxxxx@broadcom.com
Sent: Wednesday, August 24, 2011 5:37 PM
To: Windows System Software Devs Interest List
Subject: RE:[ntdev] Finding Dummy page PFN_NUMBER/PHYSICAL_ADDRESS used by system provided MDLs

Anton,

This looks like a mix of generic I/O related and paging I/O related stuff. I’m not sure what it’s trying to say.

A dummy page can never be filled with garbage, otherwise it becomes an information disclosure risc.

If an I/O operation is a read to a buffer with a number of discarded pages, the MM may decide to not bring those pages back because they’ll be overwritten anyway. BUT when those pages are locked for read op (for modification) the MM has to map distinct zero-initialized physical pages and lock them. And this is done in the usermode client context. The usermode process will see those pages.

For IoWriteAccess, the freshly committed unmodified pages MAY be mapped from a dummy zero page. This is the only case the locked MDL will see a dummy page. But THE DRIVER IS NOT ALLOWED TO MODIFY THE WRITE BUFFER ANYWAY.


NTDEV is sponsored by OSR

For our schedule of WDF, WDM, debugging and other seminars visit:
http://www.osr.com/seminars

To unsubscribe, visit the List Server section of OSR Online at http://www.osronline.com/page.cfm?name=ListServer

> A. (while in the caller’s thread context) create a duplicate MDL analogous to the system supplied MDL

You cannot, since the system-supplied MDL is created in the Mm functions, not in NtRead/WriteFile, and you have no context to re-create it.


Maxim S. Shatskih
Windows DDK MVP
xxxxx@storagecraft.com
http://www.storagecraft.com

> This part seems to be just logically inconsistent. Indeed, MM may want to read data into a dummy page X

when it locks down pages for MDL if pages Z and Y are already resident, but what on Earth does it have to do
with MDL as it is seen by a driver???

The whole article is about Mm-generated MDLs which are used to do paging reads.

Surely NtRead/WriteFile is never ever used for this path, and the MDL is handicrafted by Mm without using IoAllocateMdl.

And yes, the drivers do see the reads created by Mm for in-page operations.

The issue is that, if the read IRP’s MDL contains dummy page refs, then the decryption driver cannot use it to read the pre-decryption ciphertext - it will be aliased on the dummy page.

Thus MS suggests to allocate the separate buffer and the second MDL to read the ciphertext.


Maxim S. Shatskih
Windows DDK MVP
xxxxx@storagecraft.com
http://www.storagecraft.com

> A dummy page can never be filled with garbage, otherwise it becomes an information disclosure risc.

It does not, since it is never mapped to user address spaces.


Maxim S. Shatskih
Windows DDK MVP
xxxxx@storagecraft.com
http://www.storagecraft.com