MDL using leads to PFN BugCheck 4E

Hi all,

my driver (WDM) worked like this for years. Now, all of the sudden the driver issues a BugCheck 4E, {9a, 67762, 6, 2} when I unconnect the device with an open user space application in the background.
The user space DLL share some MDL with the kernel.

I allocate and map it like this:

`void MAP()
{

PVODI myAdress = ExAllocatePoolWithTag(NonPagedPool, MY_SIZE, '123');

mdl = IoAllocateMdl(myAdress, MY_SIZE, FALSE, FALSE, NULL);

if (!mdl)
{
	return;
}

MmBuildMdlForNonPagedPool(mdl);

userVAToReturn = MmMapLockedPagesSpecifyCache(mdl,    
	UserMode,     // Mode
	MmCached,     // Caching
	NULL,         // Address
	FALSE,        // Bugcheck?
	NormalPagePriority); // Priority  

//
// If we get NULL back, the request didn't work.
// I'm thinkin' that's better than a bug check anyday.
//
if (!userVAToReturn)  {
	MmFreePagesFromMdl(mdl);
	IoFreeMdl(mdl);
	return;
}

//
// Return the allocated pointers.
//
*UserVa = userVAToReturn;
*PMemMdl = mdl;

// store it here to free them if device will surprise removed
m_myUserVA = userVAToReturn;
m_myMddl = mdl;

}`

And if the device gets unplugged for example I call this:

`void UNMAP()
{

if (!m_myMddl) {
	return;
}

//
// Unmap the pages.
//
MmUnmapLockedPages(m_myUserVA, m_myMddl);

//
// Release the MDL.
//
IoFreeMdl(m_myMddl);
m_myMddl = NULL; 
m_myUserVA = NULL;

}`

This is part of the code was never touched in the last years and I did not write it. But well it was working OK without any issues for the last couple of years. Now ( I guess) since an Windows 10 update it starts Bugchecking with above E4 code: PFN_LIST_CORRUPT

If I do not allocate the above mdl it won’t BugCheck … Some months ago on the same PC it was working well.

So the question is, is the MDL code correct or do I missed something here ?
Is this OK to free the the mdl inside the driver and not from the UserMode dll in this case ?

Best regards,
K.Weller

Have you tried to run this driver under verifier ?

Hi Sergey,

I’ll do. I missed it unfortunately.
But beside that I nopticed something else. The allocated buffer:

myAdress 

PVODI myAdress = ExAllocatePoolWithTag(NonPagedPool, MY_SIZE, '123');

mdl = IoAllocateMdl(myAdress, MY_SIZE, FALSE, FALSE, NULL);

is a bigger block and is devided. One half is used for what ever, the other half will become the mdl.
I changed this and handle this seperate now and now it is working again. But well I’m still not sure if all in this code area is OK or not.
But I will try with the verifier and post my results.

Thanks and Reagrds,
K.Weller

“Note that if the call to MmMapLockedPages or MmMapLockedPagesSpecifyCache
specified user mode, the caller must be in the context of the original
process before calling MmUnmapLockedPages. This is because the unmapping
operation occurs in the context of the calling process, and, if the context
is incorrect, the unmapping operation could delete the address range of a
random process.”

Mark Roddy

Hi Mark,

this means I need to trigger the unmapping from the same user mode process which mapped it ?
Or is my driver the correct context and I can take care of the mapping from inside the driver during clean up for example if a “SurpriseRemoval” happens etc. So if I save pointers like I showed above and clean them up it is OK?

Or am I totally wrong in understanding this issue ?

I probably do not understand this, from the Remarks section fully and if this is related to above:

The routine returns a user address that is valid in the context of the process in which the driver is running. For example, if a 64-bit driver is running in the context of a 32-bit application, the buffer is mapped to an address in the 32-bit address range of the application.

https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/wdm/nf-wdm-mmmaplockedpagesspecifycache

@Sergey_Pisarev
verifier seems to do not find anything obviously here. So the bugcheck from above appears and verifier will not bugcheck.

thanks and Regards,
K.Weller

means I need to trigger the unmapping from the same user mode process which mapped it ?

Yes. Exactly this.

is my driver the correct context and I can take care of the mapping from inside the driver during clean up

Specific to Cleanup (as in the function that’s called when the last close of a handle is issued): Yes, cleanup is called in the context of the process that’s calling Close. If a process opens a handle to your device, and then calls Close, your driver will be called in the context of the “correct” process.

Understand, however, that there are situations in which things don’t work out well. For example, consider when a process opens your device, then your driver maps the section into the process address space. Sometime later, that process duplicates that handle to another process. Who’s to say that the other process isn’t the last one to call Close, causing your a Cleanup to run in the context of the wrong process?

This is one reason why we so strongly recommend people do not map sections back into user space… and if they do, they do it using Direct I/O and not roll their own.

Peter