I had a problem with an I/O completion routine a few days ago and for
debugging purposes inserted the completion routine from a Microsoft example
(KB326315). I found my code then failed under DriverVerifer with a pool
corruption error.
The article KB326315 claims to be the correct way to handle things, yet when
I look at everything I believe Scenario 7 and 8 in the document are wrong.
Both these pieces of code create an IRP and call IoCallDriver() on the IRP.
Their completion routines both include:
if (Irp->PendingReturned) {
IoMarkIrpPending( Irp );
}
If you look at IoMarkIrpPending, it’s a macro:
#define IoMarkIrpPending( Irp ) ( IoGetCurrentIrpStackLocation(
(Irp) )->Control |= SL_PENDING_RETURNED )
So here’s where the problem occurs, if you created the IRP and sent it,
isn’t IoGetCurrentIrpStackLocation going to be pointing at the stack
location PAST the end of the IRP, as there is no IRP stack frame for the
caller? As a result, the above code corrupts memory by setting a bit in
memory a little above your IRP. It seems like this is what I saw happen, as
DriverVerifier allocated my IRP with tail corruption checking.
Is my understanding of where the current IRP stack frame is on a completion
routine wrong? Or does this actually corrupt memory? I found a number of
previous NTDEV comments that say IoMarkIrpPending should not be called for
top level IRP’s, and that was my belief until KB326315 made me question it.
I didn’t offhand see any comments about NOT calling IoMarkIrpPending if your
the original IRP caller in the NT Insider article
http://www.osronline.com/article.cfm?id=21
It seems like CUV perhaps detects this issue, although putting an ASSERT in
the IoMarkIrpPending macro would be pretty easy.
- Jan