MmBuildMdlForNonPagedPool - BugCheck: 0x50

Hi,

My device exposes it’s memory (~512MB) via it’s BAR. My goal is to map and pass virtual address to user whenever user wants to allocate device memory to r/w directly (No cache).
In preparehardware, I get the physical address from the BAR, and pass it to MmMapIoSpace(). This maps given physical address range to non-paged system space and returns virtual address.
In an IOCTL, I do the followings
a) Pass that virtual address with user specified size to get a MDL using IoAllocateMdl().
b) To prepare the MDL for non-paged virtual memory, call MmBuildMdlForNonPagedPool().
c) On success, call MmMapLockedPagesSpecifyCache() to get a virtual address

So that I can pass it to user.

Though I could successfully get mdl from IoAllocateMdl(), my driver bugchecks (0x50 - PAGE_FAULT_IN_NONPAGED_AREA, read operation) at MmBuildMdlForNonPagedPool(). Seems like some non-paged space got freed. Do I have to add any additional work after calling MmMapIoSpace()? I thought this should already map the whole physical space to non-paged system space and keep until we explicitly unmap.

What am I missing here? Any help would be greatly appreciated.

Thanks in advance,
-Raj

Or is there any other better way to achieve my goal of exposing device memory to users? So that user can do rw directly. Or even pass it as destination buffer in file read?

Experts, please comment.

Thanks,

Your steps sound correct… how are you calling MmBuildMdlForNonPagedPool ?

The lack of replies here aren’t the result of your question being difficult or any issue with how you’ve asked it. But, rather, questions about this topic appear here so frequently that most of us are tired of answering them. To that end, you might want to search the archives here. At this point, we answered, discussed, and debated all things related to shared memory and Windows drivers over the past few years.

Peter

Thanks Peter. It appears that bugcheck 0x50 occurs due to things happen in different contexts.

Situation is MmMapIoSpace() is being called in PrepareHardware whereas I’m calling MmBuildMdlForNonPagedPool in an IOCTL by passing the MDL that I created using the virtual address returned by MmMapIoSpace.

Here is the code snippet of IOCTL code.


mdl = IoAllocateMdl((PVOID)DevContext->VirtAddress, size, FALSE, FALSE, NULL);
if (mdl == nullptr) {
TracePrint(ERROR, “Allocate Mdl failed - 0x%I64x\n”, size);
status = STATUS_INSUFFICIENT_RESOURCES;
break;
}
MmBuildMdlForNonPagedPool(mdl);


https://docs.microsoft.com/en-us/windows-hardware/drivers/debugger/bug-check-0x50--page-fault-in-nonpaged-area

As mentioned in above link, 4th parameter in my bugcheck is 0x02 which points the following description:


Type of page fault

0x03 - NONPAGED_BUGCHECK_WRONG_SESSION - An attempted reference to a session space address was made in the context of a process that has no session. Typically this means the caller is improperly trying to access a session address without correctly obtaining an object reference to the correct process and attaching to it first. This bugcheck & subtype was last used in Windows 10 RS3. In Windows 10 RS4 and above, this error is instead surfaced as 0x02 (NONPAGED_BUGCHECK_NOT_PRESENT_PAGE_TABLE).


As you mentioned, I did read many discussions from archive but unfortunately did not find any solution to my situation or I missed it.

Thanks again!
-Raj

Do you really not see the problem? The error description seems quite clear. I’m guessing you have written a KMDF driver. In that case, depending on your choice of options, your EvtIoDeviceControl is called within a callback and will run in the system process. The process that called you is no longer current, so you don’t have access its address space.

The answer is to use a EvtIoInCallerContext callback, which is an opportunity for you to handle requests with the guarantee that the calling process is still current. You can do the mapping there.

In that case, depending on your choice of options, your EvtIoDeviceControl is called within a callback and will run in the system process. >The process that called you is no longer current, so you don’t have access its address space.

The answer is to use a EvtIoInCallerContext callback, which is an opportunity for you to handle requests with the guarantee that the calling >process is still current. You can do the mapping there.

You seem to be overlooking the OP’s use of MmMapIoSpace(), i.e.a function that simply modifies a PTE without making any references to MM structures. I believe this is where his problem is rooted…

Anton Bassov

I’ve never been more confused than I am right now.

OP, please start from the beginning: What are you trying to accomplish, what fu cations are you calling, what callbacks are you calling the functions in, where does the crash occur, and what’s the output of !analyze -v … that is, if you’re trying to get us to diagnose your crash.

Peter

Hello Tim,

You are right. It is a KMDF driver. I did have my IOCTL implementation in EvtIoInCallerContext(). But the issue is, the virtual address that I get is in EvtPrepareHardware().

  1. Load driver - get the virtual address of my BAR address.
  2. Launch application - that issues an IOCTL to get device memory.
  3. EvtIoInCallerContext() gets control and execute my IOCTL implementation. Obviously contexts are different thus bugcheck at MmBuildMdlForNonPagedPool(mdl).

What are my other options to tackle this situation better?

Many thanks,
-Raj

Hmmmmm… I’ll bet that bug check is not the result of being in a different context. In all cases, you’re running in the high half of kernel virtual address space… and the addresses you’re dealing with are all in that same space… until you call MmMapLockedPagesSpecifyCache with User!ode as the parameter.

So… I’d guess you have a rather ordinary bug, where you’re calling a function wrong, or something similar.

Do everything except the MmMapLockedPages in Prepare Hardware. Do the call to MapLockedPages specifying User!ode in your In Caller Context callback.

This isn’t the hard part… the hard part is how and when you’re going to do the unmap operation… but let’s not worry about that now.

Peter

So… I’d guess you have a rather ordinary bug, where you’re calling a function wrong, or something similar.

I may be getting it all wrong, but the way I understand it, the OP does the following:

  1. Maps device BARs to the non-pageable area with MmMapIoSpace(),effectively getting a virtual address that is accessible by the CPU
  2. Calls IoAllocateMdl() with above mentioned virtual address
  3. Calls MmBuildMdlForNonPagedPool() with above mentioned MDL

The latter function, apparently, makes an assumption that the pages that target MDL describes are valid physical RAM addresses
that have their corresponding entries in PFN database. However, device BARs are not RAM pages, are they? Therefore, they don’t appear in the memory map that the OS gets from the firmware at the boot time, and,hence, they haven’t got their corresponding PFN database entries.

In other words, MmBuildMdlForNonPagedPool() seems to assume that the target MDL describes invalid physical addresses,
and bugchecks “just in case”.

In order to test my theory, what one can do is simply to map some completely bogus physical address with MmMapIoSpace(), and then take the steps that are described above. If my theory is correct you are going to get exactly the same bugcheck that the OP does.

Anton Bassov

Hello Peter,

As suggested, I have tried the followings and got the same bugcheck. :frowning:

In PrepareHardware:

  1. Find physical address of a BAR in PCIe enumeration
  2. Pass physical address into MmMapIoSpace() and get VA
  3. Pass this VA to IoAllocateMdl and get MDL
  4. Store this mdl into device context

After successful load of driver, launch application that does issue an IOCTL to get virtual address for device’s shared memory.

  1. In EvtIoInCallerContext(), retrieve mdl from device context

  2. Pass this mdl to MmBuildMdlForNonPagedPool() to prepare MDL for non-paged virtual memory.

    **** This is where the crash happens. ****

Crash dump points the issue at “nt!MI_READ_PTE_LOCK_FREE+0x4”.

Please find the attached crash dump.

Thanks,
-Raj

Hi Anton Bassov,

The latter function, apparently, makes an assumption that the pages that target MDL describes are valid physical RAM addresses
that have their corresponding entries in PFN database. However, device BARs are not RAM pages, are they? Therefore, they don’t appear in the memory map that the OS gets from the firmware at the boot time, and,hence, they haven’t got their corresponding PFN database entries.

hmm… I’m bit confused now. how does doorbell registers work? I thought device memory that gets exposed via BARs are mapped by OS kernel. Please clarify.
Otherwise, could you recommend steps to achieve my goal of getting device memory and expose it to user?

Thanks,
-Raj

Hello Anton Bassov

Thanks, but could you please clarify the following?

The latter function, apparently, makes an assumption that the pages that target MDL describes are valid physical RAM addresses
that have their corresponding entries in PFN database. However, device BARs are not RAM pages, are they? Therefore, they don’t appear in the memory map that the OS gets from the firmware at the boot time, and,hence, they haven’t got their corresponding PFN database entries.

I thought device resource that are exposed via BARs are mapped by OS kernel and accessible from it’s kernel module. That’s how all doorbell registers work, isn’t it? Please clarify.

As my goal is to expose device memory to user space, could you please provide any steps to achieve my goal?

BTW, this works in other OSes with help of mmap().

Thanks a lot!
-Raj

I thought device resource that are exposed via BARs are mapped by OS kernel and accessible from it’s kernel module.

Although device BARs may be accessed as memory addresses by the CPU, they are not RAM pages and are not treated as the ones by the OS. Accessing them by anyone, apart from their legitimate owner driver, may be dangerous even for read access. For example, consider FIFOs or read-once registers - if you read such a register you may inadvertently modify the device state transparently to its driver, with all the negative consequences that may ensue.

Therefore, unlike RAM pages, device BARs are not managed by the Memory Manager and don’t appear in PFN database.

As my goal is to expose device memory to user space

Although this is a standard approach under the OSes that support mmap(), doing so under Windows is an awfully bad idea for the security reasons. Consider what happens if an app terminates abnormally,or even better, if its address space gets modified by another process via WriteProcessMemory()

Anton Bassov

As my goal is to expose device memory to user space, could you please provide any steps to achieve my goal?

It’s complicated. Just look at the archives of this form around “sharing memory with user mode” for some of the issues, including the whole problem of duplicating handles.

And, despite some of Mr. Bassov’s apparent reservations, I’m pretty sure MmBuildMdlForNonPagedPool is the right function to use to accomplish your goal. Think about what the MDL is: It’s just a vector of PFNs, an offset into the first PFN, and maybe a currently mapped VA.

Are you running your driver under Driver Verifier (both with WDF Verifier enabled and Windows Driver Verifier enabled for your code and KMDF)? That might help.

Peter

And, despite some of Mr. Bassov’s apparent reservations, I’m pretty sure MmBuildMdlForNonPagedPool is the right
function to use to accomplish your goal.

Please note that I would NOT be questioning the validity of MmBuildMdlForNonPagedPool() for the purpose if we we speaking about the “regular” RAM. The only thing I am saying that its interactions with device BARs may be “not-so-easy”, so to say…

Think about what the MDL is: It’s just a vector of PFNs, an offset into the first PFN, and maybe a currently mapped VA.

Sure. However, please note that we are speaking about the situation when some entries of this array may be sort of invalid, at least as far as the Memory Manager is concerned…

Anton Bassov

MmBuildMdlForNonPagedPool is aware of being called with a device memory mapping and will set a bit in the resulting MDL if it doesn’t describe RAM (MDL_IO_SPACE).

OP: You’re absolutely sure you’re passing the right virtual address that was returned by MmMapIoSpace to IoAllocateMdl? Can you still dereference the pointer returned by MmMapIoSpace? What does !pte on that address say?

Also, make sure the length you’re passing to IoAllocateMdl matches the length you passed to MmMapIoSpace.

It works now… as Scott mentioned, I was passing wrong value.

Thanks all for your help.

Yay! That Scott… It’s almost like he’s psychic sometimes. :slight_smile:

Glad you got things sorted,

Peter