Skip to content
New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

WdfMemoryCreate of size BufferSize < PAGE_SIZE && BufferSize > PAGE_SIZE - sizeof(FxMemoryBuffer) crosses page boundaries #13

Open
jdallma opened this issue Feb 28, 2020 · 0 comments

Comments

@jdallma
Copy link

jdallma commented Feb 28, 2020

Creating a buffer with WdfMemoryCreate of size 4092 (and probably any value between (PAGE_SIZE - 0x7f) and (PAGE_SIZE - 1)) will not create a buffer entirely resident in a single physical page, contradicting WdfMemoryCreate documentation.

https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/wdfmemory/nf-wdfmemory-wdfmemorycreate
The documentation states the following:
If BufferSize is less than PAGE_SIZE, the operating system gives the caller exactly the number of requested bytes of memory. The buffer is not necessarily page-aligned, but it is aligned by the number of bytes that the MEMORY_ALLOCATION_ALIGNMENT constant specifies in Ntdef.h. Buffers of PAGE_SIZE or less do not cross page boundaries.
If BufferSize is PAGE_SIZE or greater, the system allocates a page-aligned buffer. If the PoolType parameter is NonPagedPool, the system allocates the number of pages that are necessary to hold all of the bytes. Any unused bytes on the last-allocated page are essentially wasted.

The sentence issue in question is "Buffers of PAGE_SIZE or less do not cross page boundaries." When allocating a WDFMEMORY of size 4092, I found the buffer begins at offset 0x80 in one page, ending at 0x7c in the next page, and therefore crossing page boundaries. The first 0x80 bytes of the page seem to contain the WDFMEMORY object (type FxMemoryObject). This is important to us as the physical addresses may not necessarily be contiguous if the buffer crosses page boundaries.

Code which encounters this issue looks like so (variable names changed and other code omitted):

typedef struct ITEM {
char[44] data; // actual fields omitted.
} ITEM, *P_ITEM;

#define NUM_ITEMS ((UINT32)(PAGE_SIZE / sizeof(ITEM))) // As many full items as fit in a single page. Change to another, smaller value if desired.
STATIC_ASSERT(NUM_ITEMS * sizeof(ITEM) <= PAGE_SIZE); // Make sure NUM_ITEMS above is sane. Later code relies on items not spanning pages.

// Note: (NUM_ITEMS * sizeof(ITEM)) == 4092. sizeof(ITEM) == 44 and NUM_ITEMS == 93.
status = WdfMemoryCreate(&attributes,
                         NonPagedPoolNx,
                         MY_MEM_TAG,
                         NUM_ITEMS * sizeof(ITEM),
                         &Ctx->MemObj,
                         &Ctx->Buffer);
// Omitted: Error handling

physBaseAddr = MmGetPhysicalAddress(Ctx->Buffer);
// An individual item should not span pages. We'll double-check the whole buffer lies in a single
// physical page here.
NT_ASSERT(((physBaseAddr.QuadPart & (PAGE_SIZE-1)) + (NUM_ITEMS * sizeof(ITEM))) <= PAGE_SIZE);

Windbg output (shortened):
kd> dx -r1 ((driver!MY_CONTEXT *)0xffffcd0bd5202750)
...
[+0x0d0] MemObj : 0x32f429df7ff8 [Type: WDFMEMORY__ *]
[+0x0d8] Buffer : 0xffffcd0bd6208080 [Type: void *]

Note, the Buffer is offset from the page boundary by 0x80. Adding 4092 to that address will cross into the next page.

kd> !wdfkd.wdfhandle 0x32f429df7ff8
...
!wdfobject 0xffffcd0bd6208000

Note, the wdfobject address is Ctx->Buffer - 0x80.

kd> dt Wdf01000!FxMemoryObject 0xffffcd0bd6208000
...
+0x00a m_ObjectSize : 0x1080
...
+0x070 m_BufferSize : 0xffc

Creating a buffer of BufferSize == PAGE_SIZE does not result in the same issue:

kd> dx -r1 (*driver!MY_CONTEXT *)0xffffcd0bd484bae0))
...
[+0x0d0] MemObj : 0x32f42bd355a8 [Type: WDFMEMORY__ *]
[+0x0d8] Buffer : 0xffffcd0bd3823000 [Type: void *]

kd> dt Wdf01000!FxMemoryObject 0xffffcd0bd42caa50
...
+0x00a m_ObjectSize : 0x80
...
+0x070 m_BufferSize : 0x1000

Note, ObjectSize is 0x80 instead of 0x1080 and Buffer ends in 0x000 rather than 0x80.

In the source for FxMemoryObject, I see memory is created from pool (and is therefore page-aligned) rather than along with the Object in the following case:

78 if (BufferSize >= PAGE_SIZE ||
79 (FxDriverGlobals->FxVerifierOn && FxDriverGlobals->FxPoolTrackingOn) ||
80 FxIsPagedPoolType(PoolType)) {

I believe that 78 will need to be changed to something like:
78 if (BufferSize >= (PAGE_SIZE – sizeof(FxMemoryBuffer))

I would submit a patch myself, but I am not familiar with the process for building and testing the WDF framework, so I would rather leave that up to the maintainers.

# for free to join this conversation on GitHub. Already have an account? # to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant