IRP_PAGING_IO problem

Information from this listserver (and other resources) has been invaluable
to me… I am trying to implement a “simple” decryption filter that
intercepts opens and reads for specific files and decrypts them on the
fly. I have had some success but only in the case of non IRP_PAGING_IO
requests.

In my IRP_MJ_READ handler, if not IRP_PAGING_IO, I allocate an Mdl for the
Irp->UserBuffer and call MmProbeAndLockPages. The MDL and certain data
describing the file offset, etc are passed to my I/O completion routine in
a context pointed structure. If IRP_PAGING_IO, I simply pass the original
MDL in the context. I then set the completion routine and call the lower
level FSD.

When my completion routine runs, if not IRP_PAGING_IO, I use
MmGetSystemAddressForMdl and that address is satisfactory for my Decrypt
routine.

How should I be handling/setting up the decryption target buffer address
when IRP_PAGING_IO?

There’s not a lot of documentation available on this sort of thing and
deriving this answer experimentally is driving my nuts…

TIA,
Bill

For paging I/O, pIrp->MdlAddress should be pointing to a valid MDL on
both the dispatch and completion routines.
MmGetSystemAddressForMdl(Safe) should give you a valid pointer. (For
non-paging I/O, be sure to watch out for IRP_MN_MDL/IRP_MN_COMPLETE
requests). Something else must be wrong here. What exactly happens?

  • Nicholas Ryan

-----Original Message-----
From: xxxxx@lists.osr.com
[mailto:xxxxx@lists.osr.com] On Behalf Of xxxxx@endwell.net
Sent: Wednesday, November 20, 2002 7:55 AM
To: File Systems Developers
Subject: [ntfsd] IRP_PAGING_IO problem

Information from this listserver (and other resources) has
been invaluable to me… I am trying to implement a “simple”
decryption filter that intercepts opens and reads for
specific files and decrypts them on the fly. I have had some
success but only in the case of non IRP_PAGING_IO requests.

In my IRP_MJ_READ handler, if not IRP_PAGING_IO, I allocate
an Mdl for the
Irp->UserBuffer and call MmProbeAndLockPages. The MDL and
certain data
describing the file offset, etc are passed to my I/O
completion routine in a context pointed structure. If
IRP_PAGING_IO, I simply pass the original MDL in the context.
I then set the completion routine and call the lower level FSD.

When my completion routine runs, if not IRP_PAGING_IO, I use
MmGetSystemAddressForMdl and that address is satisfactory for
my Decrypt routine.

How should I be handling/setting up the decryption target
buffer address when IRP_PAGING_IO?

There’s not a lot of documentation available on this sort of
thing and deriving this answer experimentally is driving my nuts…

TIA,
Bill


You are currently subscribed to ntfsd as: xxxxx@nryan.com
To unsubscribe send a blank email to %%email.unsub%%

Don’t forget that for paging I/O pIrp->UserBuffer must equal
MmGetMdlVirtualAddress(pIrp->MdlAddress) or the file systems will exhibit
odd problems.

Regards,

Tony

Tony Mason
Consulting Partner
OSR Open Systems Resources, Inc.
http://www.osr.com

-----Original Message-----
From: Nicholas Ryan [mailto:xxxxx@nryan.com]
Sent: Wednesday, November 20, 2002 2:04 PM
To: File Systems Developers
Subject: [ntfsd] RE: IRP_PAGING_IO problem

For paging I/O, pIrp->MdlAddress should be pointing to a valid MDL on
both the dispatch and completion routines.
MmGetSystemAddressForMdl(Safe) should give you a valid pointer. (For
non-paging I/O, be sure to watch out for IRP_MN_MDL/IRP_MN_COMPLETE
requests). Something else must be wrong here. What exactly happens?

  • Nicholas Ryan

-----Original Message-----
From: xxxxx@lists.osr.com
[mailto:xxxxx@lists.osr.com] On Behalf Of xxxxx@endwell.net
Sent: Wednesday, November 20, 2002 7:55 AM
To: File Systems Developers
Subject: [ntfsd] IRP_PAGING_IO problem

Information from this listserver (and other resources) has
been invaluable to me… I am trying to implement a “simple”
decryption filter that intercepts opens and reads for
specific files and decrypts them on the fly. I have had some
success but only in the case of non IRP_PAGING_IO requests.

In my IRP_MJ_READ handler, if not IRP_PAGING_IO, I allocate
an Mdl for the
Irp->UserBuffer and call MmProbeAndLockPages. The MDL and
certain data
describing the file offset, etc are passed to my I/O
completion routine in a context pointed structure. If
IRP_PAGING_IO, I simply pass the original MDL in the context.
I then set the completion routine and call the lower level FSD.

When my completion routine runs, if not IRP_PAGING_IO, I use
MmGetSystemAddressForMdl and that address is satisfactory for
my Decrypt routine.

How should I be handling/setting up the decryption target
buffer address when IRP_PAGING_IO?

There’s not a lot of documentation available on this sort of
thing and deriving this answer experimentally is driving my nuts…

TIA,
Bill


You are currently subscribed to ntfsd as: xxxxx@nryan.com
To unsubscribe send a blank email to %%email.unsub%%


You are currently subscribed to ntfsd as: xxxxx@osr.com
To unsubscribe send a blank email to %%email.unsub%%

Thanks, Tony. I believe I have observed exactly this equality, but I’m
confused by your point… This isn’t something a driver or filter has any
control over, right? Are you suggesting testing for this condition? And
doing what if it ain’t so?

Don’t forget that for paging I/O pIrp->UserBuffer must equal
MmGetMdlVirtualAddress(pIrp->MdlAddress) or the file systems will exhibit
odd problems.

Regards,

Tony

Tony Mason
Consulting Partner
OSR Open Systems Resources, Inc.
http://www.osr.com

He means that if you ever modify the pIrp->UserBuffer or
pIrp->MdlAddress fields on an existing paging I/O IRP, you must satisfy
the below constraint. (In fact, this also seems to be a requirement for
certain kinds of non-paging I/O, such as non-cached non-paging I/O.)

If you want to modify what’s being written to the file on a write
request, you will have to use your own buffer, since you can’t modify
the data in the user’s original buffer. This means you must generate
your own write IRPs or temporarily swap out the buffer on the original
request with your own.

  • Nicholas Ryan

-----Original Message-----
From: xxxxx@lists.osr.com
[mailto:xxxxx@lists.osr.com] On Behalf Of Bill
Sent: Wednesday, November 20, 2002 8:54 PM
To: File Systems Developers
Subject: [ntfsd] RE: IRP_PAGING_IO problem

Thanks, Tony. I believe I have observed exactly this
equality, but I’m confused by your point… This isn’t
something a driver or filter has any control over, right?
Are you suggesting testing for this condition? And doing
what if it ain’t so?

> Don’t forget that for paging I/O pIrp->UserBuffer must equal
> MmGetMdlVirtualAddress(pIrp->MdlAddress) or the file systems will
> exhibit odd problems.
>
> Regards,
>
> Tony
>
> Tony Mason
> Consulting Partner
> OSR Open Systems Resources, Inc.
> http://www.osr.com


You are currently subscribed to ntfsd as: xxxxx@nryan.com
To unsubscribe send a blank email to %%email.unsub%%

If *you* set Irp->MdlAddress, *you* are responsible for setting
Irp->UserBuffer. If you do not do this, it will lead to problems.

The confusing part is that Irp->UserBuffer isn’t necessarily VALID - it just
has to be THE SAME. This is because it is used as an index parameter when
breaking the MDL into partial MDLs (you can see this in
FatMultipleAsyncWrite as I recall.)

Regards,

Tony

Tony Mason
Consulting Partner
OSR Open Systems Resources, Inc.
http://www.osr.com

-----Original Message-----
From: Bill [mailto:xxxxx@endwell.net]
Sent: Wednesday, November 20, 2002 11:54 PM
To: File Systems Developers
Subject: [ntfsd] RE: IRP_PAGING_IO problem

Thanks, Tony. I believe I have observed exactly this equality, but I’m
confused by your point… This isn’t something a driver or filter has any
control over, right? Are you suggesting testing for this condition? And
doing what if it ain’t so?

Don’t forget that for paging I/O pIrp->UserBuffer must equal
MmGetMdlVirtualAddress(pIrp->MdlAddress) or the file systems will exhibit
odd problems.

Regards,

Tony

Tony Mason
Consulting Partner
OSR Open Systems Resources, Inc.
http://www.osr.com


You are currently subscribed to ntfsd as: xxxxx@osr.com
To unsubscribe send a blank email to %%email.unsub%%

Nicholas is actually right - any non-cached I/O (which includes ALL paging
I/O) must satisfy this requirement. That is because FAT/NTFS may need to
break a single I/O operation into multiple I/O operations, in which case it
uses Irp->UserBuffer as an index to know how far it is in the MDL.

Again, note that Irp->UserBuffer is never actually referenced as a pointer
in this case, so it need not be valid.

Regards,

Tony

Tony Mason
Consulting Partner
OSR Open Systems Resources, Inc.
http://www.osr.com

-----Original Message-----
From: Nicholas Ryan [mailto:xxxxx@nryan.com]
Sent: Thursday, November 21, 2002 12:36 AM
To: File Systems Developers
Subject: [ntfsd] RE: IRP_PAGING_IO problem

He means that if you ever modify the pIrp->UserBuffer or
pIrp->MdlAddress fields on an existing paging I/O IRP, you must satisfy
the below constraint. (In fact, this also seems to be a requirement for
certain kinds of non-paging I/O, such as non-cached non-paging I/O.)

If you want to modify what’s being written to the file on a write
request, you will have to use your own buffer, since you can’t modify
the data in the user’s original buffer. This means you must generate
your own write IRPs or temporarily swap out the buffer on the original
request with your own.

  • Nicholas Ryan

-----Original Message-----
From: xxxxx@lists.osr.com
[mailto:xxxxx@lists.osr.com] On Behalf Of Bill
Sent: Wednesday, November 20, 2002 8:54 PM
To: File Systems Developers
Subject: [ntfsd] RE: IRP_PAGING_IO problem

Thanks, Tony. I believe I have observed exactly this
equality, but I’m confused by your point… This isn’t
something a driver or filter has any control over, right?
Are you suggesting testing for this condition? And doing
what if it ain’t so?

> Don’t forget that for paging I/O pIrp->UserBuffer must equal
> MmGetMdlVirtualAddress(pIrp->MdlAddress) or the file systems will
> exhibit odd problems.
>
> Regards,
>
> Tony
>
> Tony Mason
> Consulting Partner
> OSR Open Systems Resources, Inc.
> http://www.osr.com


You are currently subscribed to ntfsd as: xxxxx@nryan.com
To unsubscribe send a blank email to %%email.unsub%%


You are currently subscribed to ntfsd as: xxxxx@osr.com
To unsubscribe send a blank email to %%email.unsub%%

Thanks again for answering. I wasn’t even thinking about MJ_WRITE or
fabricating my own IRPs yet, so your comment didn’t click. Now I
understand. For the moment, this is a read-only experiment.

The problem I see if I try to handle IPR_PAGING_IO read requests is: 1)
decryption failure and 2) a subsequent BugCheck some seconds later,
*seemingly* unrelated to whjat just happened - no stack trace leads back
to my driver. Here are snippets of my read and completion routines.

Note that the BugCheck occurs even if I do not attempt to Decrypt
(commented out). If I intentionally ignore the PAGING_IO case, either by
not setting the IoCompletionRoutine, or by skipping most of the processing
within same, my decryption works OK (for most files) and does not trap.

The driver exposes itself as, in this case, NEITHER_IO, same as the FSD
below.

IRP_MJ_READ:

.
.
.
readContext.offset = fileObject->CurrentByteOffset.LowPart;

if (!(Irp->Flags & IRP_PAGING_IO)) {

readContext.pMdl = IoAllocateMdl
(
Irp->UserBuffer,
currentIrpStack->Parameters.Read.Length,
FALSE, FALSE, NULL
);

__try {
MmProbeAndLockPages( readContext.pMdl, KernelMode, IoModifyAccess );
}
__except( EXCEPTION_EXECUTE_HANDLER) {
IoFreeMdl( readContext.pMdl );
return GetExceptionCode();
}
}
else {
// cannot Probe and Lock on a PAGING_IO request, just pass the MDL

DbgPrint( “PAGING_IO read”);
readContext.pMdl = Irp->MdlAddress;
}
// Before calling the FSD driver with the request, set a completion
// routine where we can do our thang.

IoSetCompletionRoutine
(
Irp,
PFFReadCompletionRoutine,
&readContext,
TRUE,
TRUE,
TRUE
);

*nextIrpStack = *currentIrpStack;

lowerStatus = IoCallDriver( hookExt->FileSystem, Irp );
return lowerStatus;

My PFFReadCompletionRoutine:

.
.
.
if (Irp->IoStatus.Status == STATUS_SUCCESS) {

if (len > 0) { // only Decrypt if we read anything

buffer = MmGetSystemAddressForMdl( pReadContext->pMdl );

DbgPrint( “Decrypt %d bytes at address %08x from offset %08x”, len,
buffer, pReadContext->offset );
DbgPrint( “buffer address: %08x, user buffer address: %08x”, buffer,
Irp->UserBuffer );

// Decrypt( buffer, len, pReadContext->offset );
}
else {
DbgPrint( “NOT decrypting: non-positive length from FSD” );
}
}
else {
DbgPrint( “NOT decrypting: non-zero rc from FSD: %08x”,
Irp->IoStatus.Status );
}
if (Irp->Flags & IRP_PAGING_IO) {
;
}
else {
MmUnlockPages( pReadContext->pMdl );
IoFreeMdl( pReadContext->pMdl );
}

if (Irp->PendingReturned) {
IoMarkIrpPending( Irp );
}
return( STATUS_SUCCESS );

I’m not exactly a newbie to device drivers, having written them for AIX,
Netware, OS/2 and Windows 9x/NT, but this is my first crack at something
like a filter driver.

I’m also pretty sure this is something simple I’m missing (still waiting
on
my IFS kit to come…)

If *you* set Irp->MdlAddress, *you* are responsible for setting
Irp->UserBuffer. If you do not do this, it will lead to problems.

The confusing part is that Irp->UserBuffer isn’t necessarily VALID - it just
has to be THE SAME. This is because it is used as an index parameter when
breaking the MDL into partial MDLs (you can see this in
FatMultipleAsyncWrite as I recall.)

Regards,

Tony

Tony Mason
Consulting Partner
OSR Open Systems Resources, Inc.
http://www.osr.com

In the code snippet I appended (prettied up for posting) I inadvertently
swapped the call to IoSetCompletionRoutine and the copy of the stack data
before IoCallDriver. This was only in my post and not in the code itself.
It is not the source of the problem I was asking about…

In my initial pass I noticed at least one glaring error (that probably
doesn’t cause it to crash):

Do not assign one stack location to the next. Use
IoCopyCurrentIrpStackLocationToNext.

And then back to your original question:

It is OK to decrypt in place. But your code assumes Irp->UserBuffer, which
is not necessarily valid, particularly for paging I/O operations. Instead,
you should look to see if Irp->MdlAddress is already set. If it is, use the
MDL, not the user buffer.

The important point here is to keep in mind that at the point your filter
gets a paging I/O IRP, that MDL is the only thing that really matters - it
describes the physical memory that will EVENTUALLY be mapped. But it is not
mapped yet (otherwise, some process might try to access it.) So you need to
focus on using the MDL to satisfy the operation.

Does that make more sense?

Regards,

Tony

Tony Mason
Consulting Partner
OSR Open Systems Resources, Inc.
http://www.osr.com

-----Original Message-----
From: Bill [mailto:xxxxx@endwell.net]
Sent: Thursday, November 21, 2002 9:29 AM
To: File Systems Developers
Subject: [ntfsd] RE: IRP_PAGING_IO problem

Thanks again for answering. I wasn’t even thinking about MJ_WRITE or
fabricating my own IRPs yet, so your comment didn’t click. Now I
understand. For the moment, this is a read-only experiment.

The problem I see if I try to handle IPR_PAGING_IO read requests is: 1)
decryption failure and 2) a subsequent BugCheck some seconds later,
*seemingly* unrelated to whjat just happened - no stack trace leads back
to my driver. Here are snippets of my read and completion routines.

Note that the BugCheck occurs even if I do not attempt to Decrypt
(commented out). If I intentionally ignore the PAGING_IO case, either by
not setting the IoCompletionRoutine, or by skipping most of the processing
within same, my decryption works OK (for most files) and does not trap.

The driver exposes itself as, in this case, NEITHER_IO, same as the FSD
below.

IRP_MJ_READ:

.
.
.
readContext.offset = fileObject->CurrentByteOffset.LowPart;

if (!(Irp->Flags & IRP_PAGING_IO)) {

readContext.pMdl = IoAllocateMdl
(
Irp->UserBuffer,
currentIrpStack->Parameters.Read.Length,
FALSE, FALSE, NULL
);

__try {
MmProbeAndLockPages( readContext.pMdl, KernelMode, IoModifyAccess );
}
__except( EXCEPTION_EXECUTE_HANDLER) {
IoFreeMdl( readContext.pMdl );
return GetExceptionCode();
}
}
else {
// cannot Probe and Lock on a PAGING_IO request, just pass the MDL

DbgPrint( “PAGING_IO read”);
readContext.pMdl = Irp->MdlAddress;
}
// Before calling the FSD driver with the request, set a completion
// routine where we can do our thang.

IoSetCompletionRoutine
(
Irp,
PFFReadCompletionRoutine,
&readContext,
TRUE,
TRUE,
TRUE
);

*nextIrpStack = *currentIrpStack;

lowerStatus = IoCallDriver( hookExt->FileSystem, Irp );
return lowerStatus;

My PFFReadCompletionRoutine:

.
.
.
if (Irp->IoStatus.Status == STATUS_SUCCESS) {

if (len > 0) { // only Decrypt if we read anything

buffer = MmGetSystemAddressForMdl( pReadContext->pMdl );

DbgPrint( “Decrypt %d bytes at address %08x from offset %08x”, len,
buffer, pReadContext->offset );
DbgPrint( “buffer address: %08x, user buffer address: %08x”, buffer,
Irp->UserBuffer );

// Decrypt( buffer, len, pReadContext->offset );
}
else {
DbgPrint( “NOT decrypting: non-positive length from FSD” );
}
}
else {
DbgPrint( “NOT decrypting: non-zero rc from FSD: %08x”,
Irp->IoStatus.Status );
}
if (Irp->Flags & IRP_PAGING_IO) {
;
}
else {
MmUnlockPages( pReadContext->pMdl );
IoFreeMdl( pReadContext->pMdl );
}

if (Irp->PendingReturned) {
IoMarkIrpPending( Irp );
}
return( STATUS_SUCCESS );

I’m not exactly a newbie to device drivers, having written them for AIX,
Netware, OS/2 and Windows 9x/NT, but this is my first crack at something
like a filter driver.

I’m also pretty sure this is something simple I’m missing (still waiting
on
my IFS kit to come…)

If *you* set Irp->MdlAddress, *you* are responsible for setting
Irp->UserBuffer. If you do not do this, it will lead to problems.

The confusing part is that Irp->UserBuffer isn’t necessarily VALID - it
just
has to be THE SAME. This is because it is used as an index parameter when
breaking the MDL into partial MDLs (you can see this in
FatMultipleAsyncWrite as I recall.)

Regards,

Tony

Tony Mason
Consulting Partner
OSR Open Systems Resources, Inc.
http://www.osr.com


You are currently subscribed to ntfsd as: xxxxx@osr.com
To unsubscribe send a blank email to %%email.unsub%%

> In my initial pass I noticed at least one glaring error (that probably

doesn’t cause it to crash):

Do not assign one stack location to the next. Use
IoCopyCurrentIrpStackLocationToNext.

Funny you point that out. I’ve seen it done both ways, and I’ve done it
both ways. The real point is… I believe the OS wouldn’t provide such an
API if it wasn’t better to use it. I think I’ll change my style on that
one.

And then back to your original question:

It is OK to decrypt in place. But your code assumes Irp->UserBuffer, which
is not necessarily valid, particularly for paging I/O operations. Instead,
you should look to see if Irp->MdlAddress is already set. If it is, use the
MDL, not the user buffer.

My code ASSumed UserBuffer because, at the time, I am neither I/O. But I
see that status could change (maybe without my knowing it). I gather I
should used MdlAddress if not NULL and MmGetSystemAddressForMdlSafe.

The important point here is to keep in mind that at the point your filter
gets a paging I/O IRP, that MDL is the only thing that really matters - it
describes the physical memory that will EVENTUALLY be mapped. But it is not
mapped yet (otherwise, some process might try to access it.) So you need to
focus on using the MDL to satisfy the operation.

Does that make more sense?

Yes, it do. And I thank you for your patient and (as always) insightful
reply.

One last question (for now)… If I don’t actually do the Decrypt in the
completion routine, but suspend the MJ_READ Dispatch waiting an event set
by the completion routine, am I right in thinking I can pick back up in
the original context with a valid system address for the target buffer?
Would save the ProbeAndLock, right (which I cannot do in PAGING_IO
anyway)? Only need then is to wrap the Decrypt function in a try-except?

Regards,

Tony