Something about MDL and Page Lock

Hi,

I’m now wondering something about MDL…

Now I’ve a buffer named buf, which is allocated by ExAllocatePool.

buf = ExAllocatePool(…);

Next, I use a MDL to describe this buffer and lock the pages related to this buffer:

mdl = IoAllocateMdl(…);
MmProbeAndLockPages(mdl, …);

Now comes something I’m not very sure.

If I somewhere use ExFreePool to free buf, and again use the memory described by mdl in some other places, to say in another thread, shall I get an access violation?

// in one thread or one place in the driver
ExFreePool(buf);

// in another thread or another place in the same driver, and after ExFreePool above
buf2 = MmGetSystemAddressForMdlSafe(mdl, …);
… …// use buf2
MmUnlockPages(mdl, …)
IoFreeMdl(…);

I’ve tried this, and got no access violation.

To my mind, the MmProbeAndLockPages function will lock the physical pages related to buf memory, so even we free buf, we still could access the locked pages. This would be the same regardless of what type the buf is: PagedPool or NonPagedPool.

Could someone tell me whether my analysis is all right?

Thanks in advance.

Try it with driver verifier turned on.

-----Original Message-----
From: xxxxx@lists.osr.com
[mailto:xxxxx@lists.osr.com] On Behalf Of zxpsuper@163.com
Sent: Tuesday, July 17, 2007 10:05 AM
To: Windows System Software Devs Interest List
Subject: [ntdev] Something about MDL and Page Lock

Hi,

I’m now wondering something about MDL…

Now I’ve a buffer named buf, which is allocated by ExAllocatePool.

buf = ExAllocatePool(…);

Next, I use a MDL to describe this buffer and lock the pages related to
this buffer:

mdl = IoAllocateMdl(…);
MmProbeAndLockPages(mdl, …);

Now comes something I’m not very sure.

If I somewhere use ExFreePool to free buf, and again use the memory
described by mdl in some other places, to say in another thread, shall I
get an access violation?

// in one thread or one place in the driver
ExFreePool(buf);

// in another thread or another place in the same driver, and after
ExFreePool above
buf2 = MmGetSystemAddressForMdlSafe(mdl, …);
… …// use buf2
MmUnlockPages(mdl, …)
IoFreeMdl(…);

I’ve tried this, and got no access violation.

To my mind, the MmProbeAndLockPages function will lock the physical
pages related to buf memory, so even we free buf, we still could access
the locked pages. This would be the same regardless of what type the buf
is: PagedPool or NonPagedPool.

Could someone tell me whether my analysis is all right?

Thanks in advance.


Questions? First check the Kernel Driver FAQ at
http://www.osronline.com/article.cfm?id=256

To unsubscribe, visit the List Server section of OSR Online at
http://www.osronline.com/page.cfm?name=ListServer

> To my mind, the MmProbeAndLockPages function will lock the physical pages

related to buf memory, so even we free buf, we still could access the locked pages.

MDL describes *PHYSICAL* pages, while a pointer that ExAllocatePool() returns describes a virtual address. After you have freed a buffer, virtual address becomes invalid, but physical pages that corresponded to this address are not going anywhere, so that they still may get accessed. However, after you have freed a buffer a new lifetime starts for these physical pages, so that, by the time you map MDL to memory, they may well correspond to a totally different virtual address - after all, MSDN says that MmProbeAndLockPages() locks pages in RAM, but it does not say that it prevents them from being recycled if the virtual address that currently describes these pages gets invalidated. If this is the case, your code is a good example of how a buggy driver may corrupt memory. Bugs like that are not that easy to trace -you may BSOD at some later stage, probably even after the buggy has been already unloaded.

Anton Bassov

> If I somewhere use ExFreePool to free buf, and again use the memory

described by mdl in some other places, to say in another thread, shall I get
an
access violation?

Please unlock and destroy the MDL before ExFreePool.

Also, if you allocate nonpaged memory, then use MmBuildMdlForNonPagedPool
(which cannot fail and needs no undo) instead of MmProbeAndLockPages.


Maxim Shatskih, Windows DDK MVP
StorageCraft Corporation
xxxxx@storagecraft.com
http://www.storagecraft.com

I’ve tried Driver Verifier, and nothing wrong happened.

To anton bassov:

You mean it is not clear whether the Windows Kernel would recycle the physical pages whose corresponding virtual memory has been invalidated (that is to say it is freed by ExFreePool), is it?

If Windows would recycle those pages, the next time I accessed those pages using a totally different virtual address which was returned by MmGetSystemAddressForMdlSafe, I could get a somehow different content. Is it?

To Maxim S. Shatskih:

Thanks for your advice. But what I mean is whether we could unlock and destroy the MDL after ExFreePool. Here is the situation to use it:

I’ve a driver which has one thread sending some data to anther thread. To be simple, let’s call the data-sending thread TS while the data-receiving thread TR. TR would take some time to tackle the data, and I don’t want TS to wait. What I do is just let TR create a new MDL and lock the pages correponding to the virtual memory sent by TS. Althought TR have not finished all the data it’s received, it could just return such as STATUS_PENDING to TS. TS would free the buffer, and send new data to TS.

Is it possible to do so?

Thanks to all of you!

> You mean it is not clear whether the Windows Kernel would recycle the physical

pages whose corresponding virtual memory has been invalidated (that is to say it
is freed by ExFreePool), is it?

I haven’t seen any doc that states that physical pages locked by MmProbeAndLockPages() are guaranteed to be left alone until they are freed by MmUnlockPages(), no matter what - the only thing MSDN says that they will get locked in RAM. Whenever you are in a “gray area”, it is always a good idea to make the most restrictive assumption. Therefore, in practical terms, I would assume that they may get reused if virtual address that currently describes them gets invalidated - even if my assumption turns out to be wrong, it is not going to do me any harm. However, if I assume the opposite and my assumption turns out to be wrong, I have a good chance to corrupt memory…

I’ve a driver which has one thread sending some data to anther thread. To be
simple, let’s call the data-sending thread TS while the data-receiving thread
TR. TR would take some time to tackle the data, and I don’t want TS to wait.
What I do is just let TR create a new MDL and lock the pages correponding to the
virtual memory sent by TS. Althought TR have not finished all the data it’s
received, it could just return such as STATUS_PENDING to TS. TS would free the
buffer, and send new data to TS.

Is it possible to do so?

Well, as you have already established experimentally, it is possible - the only question is how safe it is. It would not do it…

Anton Bassov

>is just let TR create a new MDL and lock the pages correponding to the virtual

memory sent by TS.

…and also transfer the memory ownership to TR.

STATUS_PENDING to TS. TS would free the buffer, and send new data to TS.

No, it should not free the buffer, the ownership should be transferred to TR.


Maxim Shatskih, Windows DDK MVP
StorageCraft Corporation
xxxxx@storagecraft.com
http://www.storagecraft.com

Thanks to all of you. I’m now trying to work around this problem.

Things I mentioned above are all about a TDI client (that is a kernel mode socket) I implemented by myself. Now the problem is the effeciency of TCP sending. I’ve found the cause of it is the KeWaitForSingleObject after the invocation of IoCallDriver. Here’s the code segment:

NTSTATUS tdi_send_stream(PFILE_OBJECT connectionFileObject, const char *buf, int len, ULONG flags)
{

… …

status = IoCallDriver(devObj, irp);

if (status == STATUS_PENDING)
{
KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL);
status = iosb.Status;
}

… …
}

The KeWaitForSingleObject would not return until the other side of the communication ACKed the packages sent by my socket. Therefore it slows the socket’s sending speed.

Now I’m trying to not wait for the completion of the IRP and wanna allocate some memory in tdi_send_stream function and the new allocated memory will hold the content of the “buf”. Therefore those who invocate tdi_send_stream could free the “buf” and continue to send the following data.

I think it wolud do some help to improve the socket’s sending speed. Would someone give me some advice on whether it would make sense? Thanks a lot!

> I think it wolud do some help to improve the socket’s sending speed.

Correct. This is what SO_SNDBUF does.


Maxim Shatskih, Windows DDK MVP
StorageCraft Corporation
xxxxx@storagecraft.com
http://www.storagecraft.com