Mapping kernel virtual address in user space

[quote]
Unfortunately, this may not be the right place.

[quote]

Right. Do you have a sense of deja vu? We’ve had this exact same discussion here about a zillion times.

/continues on

Hence the purpose of PsRegisterProcessCreateNotificationCallback or whatever it’s called. See the article.

I’m not saying this isn’t a PITA, by the way.

Which is reliable, assuming you understand how to handle cancel. Which, like, almost nobody but an does. Including about half the people who THINK they understand cancel. This is demonstrated clearly by the number of crashes seen in driver cancel code.

If you understand cancel, and are happy to handle it in your driver, that approach will definitely work and I’m all for it. If not, I most heartily recommend handling CLEANUP.

Peter
OSR

Anton is right.

The PROCESS_HAS_LOCKED_PAGES bugcheck happens if a driver probe-and-locks pages in a process and then the process exits.

In the case where a driver maps locked kernel memory into a process using MmMapLockedPagesSpecifyCache (UserMode), and then the process exits, there will be no bugcheck. You don’t need to explicitly unmap the user mapping before the process terminates, the memory manager does this automatically.

The part that needs to be handled carefully is unlocking and/or freeing the kernel pages. If you want to do that you have to explicitly unmap the MDL from every process into which it was mapped. That means you have to keep a referenced pointer to each process and the base VA where the MDL was mapped, and use KeStackAttachProcess followed by MmUnmapLockedPages. And then you’ll probably want to be notified when a process exits (using IRP_MJ_CLEANUP or a Ps callback) so you can release your reference to it.

If you leave a stale user mapping pointing to pages that have been freed or unlocked the process will be able to access random kernel memory. This is the main security risk here (not the possibility of a bugcheck).

-----Original Message-----
From: xxxxx@lists.osr.com [mailto:xxxxx@lists.osr.com] On Behalf Of xxxxx@broadcom.com
Sent: Wednesday, October 05, 2011 1:00 PM
To: Windows System Software Devs Interest List
Subject: RE:[ntdev] Mapping kernel virtual address in user space

@Anton:

Bug Check 0x76: PROCESS_HAS_LOCKED_PAGES

You’ll see it if the client process exits while the driver forgot to unmap the pages.


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

With all due respect, I’ve SEEN this exact scenario bugcheck. It might not bug check anymore, but I *swear* I’ve personally seen this happen in my own code. Am I insane?

Which is why you never map non-paged pool back to user space. Period.

Peter
OSR

Peter,

I suspect you were either seeing a different problem (not the PROCESS_HAS_LOCKED_PAGES bugcheck), or you misdiagnosed what was causing it.

The only two cases where the PROCESS_HAS_LOCKED_PAGES bugcheck can be generated are:

  1. Somebody tries to unlock an MDL that is not actually locked (in this case the first argument is 1… This should probably be a different bugcheck code altogether)

  2. EPROCESS->NumberOfLockedPages is non-zero when process address space is deleted.

NumberOfLockedPages is only incremented inside MmProbeAndLockPages. So the only way to hit a PROCESS_HAS_LOCKED_PAGES on process exit is by forgetting to unlock an MDL built using MmProbeAndLockPages.

I checked sources going back to NT 3.5 and it’s always worked like that (with minor variations… E.g. NT 3.5 would only bugcheck on CHK builds).

Thanks,
Pavel

-----Original Message-----
From: xxxxx@lists.osr.com [mailto:xxxxx@lists.osr.com] On Behalf Of xxxxx@osr.com
Sent: Wednesday, October 05, 2011 7:39 PM
To: Windows System Software Devs Interest List
Subject: RE:[ntdev] Mapping kernel virtual address in user space

With all due respect, I’ve SEEN this exact scenario bugcheck. It might not bug check anymore, but I *swear* I’ve personally seen this happen in my own code. Am I insane?

Which is why you never map non-paged pool back to user space. Period.

Peter
OSR


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

Thanks Pavel. I definitely appreciate your efforts.

But I wasn’t saying the bugcheck I saw was necessarily PROCESS_HAS_LOCKED_PAGES, but rather forgetting to unmap the some memory that a driver mapped into user address space (with MmMapLockedPagesSpecifyCache(UserMode) before the process exits, has caused a Blue Screen.

Like I said, I swear I’ve seen it. But, I could certainly misremember or… as you said… have seen some other problem and misdiagnosed the cause.

Thanks again,

Peter
OSR

This whole thread seems to focus on a complex, convoluted solution to what
should be a simple problem.

The basic failed premise is that the buffer should be allocated by the
kernel, and if I remember this too-long thread correctly, that was so a
single “contiguous” page could be allocated.

There are two vastly simpler solutions that are well-supported. One is to
let the user pass in a buffer using DO_DIRECT_IO or METHOD_[IN,
OUT]_DIRECT. This buffer might not be page-aligned so it might require
two DMA operations to fill it (I’m assuming this is some device without
scatter/gather). The other method is the same, but requires the caller to
pass in a page-aligned address, and returns STATUS_INVALID_PARAMETER if it
is not. Then a single DMA will work. The buffer could be allocated using
VirtualAlloc, which allocates page-aligned (actually, the current system’s
allocation granularity, which this week, for x86 systems, is nominally
64k).

To use Peter’s analogy, this seems to be a debate about the right glue to
fasten wings to a pig. Nobody seems to just come out and say “This is a
really bad idea, don’t do it this way!”

I think the OP really doesn’t grasp the relationship of physical and
virtual memory.
joe

Peter,

I suspect you were either seeing a different problem (not the
PROCESS_HAS_LOCKED_PAGES bugcheck), or you misdiagnosed what was causing
it.

The only two cases where the PROCESS_HAS_LOCKED_PAGES bugcheck can be
generated are:

  1. Somebody tries to unlock an MDL that is not actually locked (in this
    case the first argument is 1… This should probably be a different
    bugcheck code altogether)

  2. EPROCESS->NumberOfLockedPages is non-zero when process address space is
    deleted.

NumberOfLockedPages is only incremented inside MmProbeAndLockPages. So the
only way to hit a PROCESS_HAS_LOCKED_PAGES on process exit is by
forgetting to unlock an MDL built using MmProbeAndLockPages.

I checked sources going back to NT 3.5 and it’s always worked like that
(with minor variations… E.g. NT 3.5 would only bugcheck on CHK builds).

Thanks,
Pavel

-----Original Message-----
From: xxxxx@lists.osr.com
[mailto:xxxxx@lists.osr.com] On Behalf Of xxxxx@osr.com
Sent: Wednesday, October 05, 2011 7:39 PM
To: Windows System Software Devs Interest List
Subject: RE:[ntdev] Mapping kernel virtual address in user space

With all due respect, I’ve SEEN this exact scenario bugcheck. It might
not bug check anymore, but I *swear* I’ve personally seen this happen in
my own code. Am I insane?

Which is why you never map non-paged pool back to user space. Period.

Peter
OSR


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


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

Did you read the whole thread? I addressed this approach earlier… it’s not a panacea:



>


>
>Which is reliable, assuming you understand how to handle cancel. Which, like,
>almost nobody but an does. Including about half the people who THINK they
>understand cancel. This is demonstrated clearly by the number of crashes seen
>in driver cancel code.
>
>If you understand cancel, and are happy to handle it in your driver, that
>approach will definitely work and I’m all for it. If not, I most heartily
>recommend handling CLEANUP.

So… handle cancel or just do CLEANUP and don’t worry about the weird, almost never used, edge condition where somebody clones a process. Personally, I vote for the latter. It’s an approach most people can get right and it’s easily testable.

Peter
OSR

This article unfortunately has a couple of problems.

First, the code sample has a bug where it doesn’t handle MmMapLockedPagesSpecifyCache failures correctly. For user space mappings this API raises an exception, so the code should be using try/except instead of checking for NULL.

Second, the article doesn’t mention the security issue that will occur if the driver frees or unlocks memory before unmapping it, or how to prevent it.

In fact, it almost pushes people towards this security hole, because it recommends calling MmUnmapLockedPages/MmFreePagesFromMdl in response to IRP_MJ_CLEANUP, and downplays the importance of handling duplicated/inherited handles correctly.

Here’s what can happen if a driver ignores these issues:

  1. Driver allocates memory (MmAllocatePagesForMdlEx) and maps it into unprivileged process A. Process A can now read and modify this memory but that’s not a problem (yet) because the driver never puts anything sensitive there.

  2. Attacker duplicates the device handle from process A into process B, closes the original, then closes the copy.

  3. Driver receives IRP_MJ_CLEANUP in the context of process B. It calls MmUnmapLockedPages which silently fails because process B has no VAD matching the specified base address.

  4. Driver thinks the memory is no longer mapped anywhere, so it frees it (MmFreePagesFromMdl).

  5. Attacker can now read and modify random memory through the stale view in process A.

To avoid this the driver needs to either use something like Ps process exit notification (which is guaranteed to be delivered in the right process context), or keep a referenced pointer to process A and attach to it before unmapping the MDL. The latter is probably more flexible because it allows the driver to cleanup its resources without forcing the process to exit.

-----Original Message-----
From: xxxxx@lists.osr.com [mailto:xxxxx@lists.osr.com] On Behalf Of xxxxx@osr.com
Sent: Wednesday, October 05, 2011 9:45 AM
To: Windows System Software Devs Interest List
Subject: RE:[ntdev] Mapping kernel virtual address in user space

We’ve discussed this here on NTDEV a thousand times. You can search the archives, and you can also refer to this article which gives you a WDM-based example of what you need to do… the original article was written several years back, but what updates in January of this year:

http://www.osronline.com/article.cfm?id=39

I disagree… See my other reply.

Nice pickup. I’ll fix that, thanks.

Sigh! Yes, yes… this is mentioned in the article. But there are problems even with this approach: For example, there’s a limit on the number of drivers that can register for this callback. This is no cure all.

So, you suggest getting a pointer to the EPROCESS of Process A, referencing it, and then on CLEANUP doing KeStackAttachProcess to Process A’s context to do the unmap?

Doesn’t that delay Process A’s exit until Process B exits in the case where Process A closes the handle and exits before Process B?

Peter
OSR

> Doesn’t that delay Process A’s exit until Process B exits in the case where Process A closes

the handle and exits before Process B?

Not at all - it will just ensure that its EPROCESS remains valid (because of non-zero refcount). However, the process itself will be in terminated state, so that if someone waits on it this wait will get satisfied…

Anton Bassov

…but the process won’t be torn-down, and pages won’t be freed?

I’m not looking for guesses, Anton… so, unless you absolutely know please do me a favor here and don’t merely surmise.

Peter
OSR

Peter,

…but the process won’t be torn-down, and pages won’t be freed? I’m not looking for guesses, Anton…
so, unless you absolutely know please do me a favor here and don’t merely surmise.

Actually, I am not guessing - all my thoughts are based only upon the statement that Pavel made on this thread, namely, upon the one that MmUnmapLockedPages() is going to fail"politely" if VAD is invalid in context of the target process (which, BTW, seems to a bit strange to me, but anyway).

As long as our target EPROCEESS is still valid our call to KeStackAttachProcess() is going to be successful, right. Therefore, even if the process gets torn down and our target VADs get nvalidated, making a call to MmUnmapLockedPages() after KeStackAttachProcess() is safe - the worst thing we may possibly expect here is a silent failure that, in this context, we don’t care about anyway…

Anton Bassov