Using IoBuildPartialMdl causes PAGE_FAULT_BEYOND_END_OF_ALLOCATION (cd)

Hi,

So I was having a problem in Vista, but read something on special pool that Win7 did a better job of handling IoAllocateMdl and such, so I’ve moved over to debugging on that and yes, it seems to have found the problem. Now reading the docs I don’t see why this happens but clearly there is something not documented on how to use the IoBuildPartialMdl. Here’s the cause that faults when calling the IoBuildPartialMdl call. (numberofbytes=0x4000, offset==0)

PAGE_FAULT_BEYOND_END_OF_ALLOCATION (cd)
N bytes of memory was allocated and more than N bytes are being referenced.
This cannot be protected by try-except.
When possible, the guilty driver’s name (Unicode string) is printed on
the bugcheck screen and saved in KiBugCheckDriver.
Arguments:
Arg1: fffff98004325000, memory referenced
Arg2: 0000000000000001, value 0 = read operation, 1 = write operation
Arg3: fffff8000289cb81, if non-zero, the address which referenced memory.
Arg4: 0000000000000000, Mm internal code.

 PIRP irp;
 if ((irp = IoAllocateIrp(FileCtx.DeviceObject->StackSize, FALSE)) != NULL) {
   PMDL mdl;
   if ((mdl = IoAllocateMdl(NULL, numberofbytes, FALSE, FALSE, irp)) != NULL) {
     UINT_PTR offset = UINT_PTR(databuffer) - UINT_PTR(srb->DataBuffer);
     // create the partial mdl over the original
     IoBuildPartialMdl(((SRB_EX*)srb->SrbExtension)->OriginalMdl, mdl, (PUCHAR) MmGetMdlVirtualAddress(((SRB_EX*)srb->SrbExtension)->OriginalMdl)+offset, numberofbytes);

How is the allocation and IoBuildPartialMdl supposed to work so it doesn’t overrun the buffer?

It seems happier with the following. Is that the proper way?

 PIRP irp;
 if ((irp = IoAllocateIrp(FileCtx.DeviceObject->StackSize, FALSE)) != NULL) {
   // calculate virtual address in relation to the original virtual address for the original mdl
   UINT_PTR offset = UINT_PTR(databuffer) - UINT_PTR(srb->DataBuffer);
   PUCHAR orgmdlvirtualaddress = (PUCHAR) MmGetMdlVirtualAddress(((SRB_EX*)srb->SrbExtension)->OriginalMdl)+offset;
   // allocate for new partial mdl
   PMDL mdl;
   if ((mdl = IoAllocateMdl(orgmdlvirtualaddress, numberofbytes, FALSE, FALSE, irp)) != NULL) {
     // create the partial mdl over the original
     IoBuildPartialMdl(((SRB_EX*)srb->SrbExtension)->OriginalMdl, mdl, orgmdlvirtualaddress, numberofbytes);

I’m not sure what that first argument you’re passing to MmGetMdlVirtualAddress resolves to in that first code clip you showed? Aren’t you getting the address of the MDL itself and adding “offset” to it?? That code, with the parens and the function call as an argument on run together, is butt ugly… I know that much.

Now I know why I almost never read code clips posted here. Sigh…

Peter

Ditto on the ugly part, I cannot figure anything :wink:

The one thing I can see as a question mark is why are you passing
NULL as the first argument to IoAllocateMdl in your first example??

On 2/23/19, Peter_Viscarola_(OSR)
wrote:

> I’m not sure what that first argument you’re passing to
> MmGetMdlVirtualAddress resolves to in that first code clip you showed?
> Aren’t you getting the address of the MDL itself and adding “offset” to it??
> That code, with the parents and the function call as an argument on run
> together, is butt ugly… I know that much.
>
> Now I know why I almost never read code clips posted here. Sigh…

@“Peter_Viscarola_(OSR)” said:
I’m not sure what that first argument you’re passing to MmGetMdlVirtualAddress resolves to in that first code clip you showed?

There is only one argument. The original mdl of the SRB request. and adds in an offset because the buffers used come from StorPortGetSystemAddress() and that is what is the UserBuffer in the SRB is (original saved in the context). The documentation of IoBuildPartialMdl says you need to use the MmGetMdlVirtualAddress and not the system virtual address.

@Dejan_Maksimovic said:

The one thing I can see as a question mark is why are you passing
NULL as the first argument to IoAllocateMdl in your first example??

Because the sample usage I have / had uses NULL there. And the docs say it’s optional.

You’re allocating a variable length MDL to describe a virtually contiguous data buffer in terms of its underlying physical pages. Specifying the correct starting virtual address is important as it might impact the number of physical pages the buffer spans.

Trivia Question: What macro does the WDK provide to determine the number of physical pages required for a given virtual address and length?

Answer:

!
! ADDRESS_AND_SIZE_TO_SPAN_PAGES

@“Scott_Noone_(OSR)” said:
You’re allocating a variable length MDL to describe a virtually contiguous data buffer in terms of its underlying physical pages. Specifying the correct starting virtual address is important as it might impact the number of physical pages the buffer spans.

Ok, sorta like setting up scatter / gather lists, the NULL must just presume page aligned address. I would guess that if you only used the offset it would work as well (0->(pagesize-1)). But okay, the new method works, so thanks.