Driver created IRP's and CancelSynchronousIo

I have an FSD (file system driver and not filter driver) that responds to special FSCTL (IRP_MJ_FILE_SYSTEM_CONTROL) requests. The driver uses a variant of FsRtlCancellableWaitForSingleObject to perform a cancellable wait on a kernel object and the FSCTL IRP.

I now have a second driver that wishes to send an FSCTL IRP to the FSD and a requirement that this must be done in a manner so that CancelSynchronousIo is functional. My question is: how should I build the IRP so that it can be cancelled with CancelSynchronousIo?

At first I built the FSCTL IRP using IoAllocateIrp. This method does not create a threaded IRP and therefore cannot be canceled with CancelSynchronousIo. (There is IoQueueThreadIrp but it is reserved for system use.)

Now I am using IoBuildDeviceIoControlRequest and patch the returned IRP fields with IRP_MJ_FILE_SYSTEM_CONTROL/IRP_MN_KERNEL_CALL, etc. because IoBuildDeviceIoControlRequest creates IRP_MJ_DEVICE_CONTROL IRP’s. This creates a threaded IRP (confirmed with !thread). Unfortunately CancelSynchronousIo still cannot cancel the IRP.

The problem is that although the internal IopCancelIrpsInCurrentThreadListSpecialApc finds the IRP in the thread’s IrpList, the IRP is not marked IRP_SYNCHRONOUS_API and is therefore ignored. My obvious next move is to add that bit to Irp->Flags, but I am beginning to wonder if I should not be doing what I am doing.

I appreciate any advice you may have on the subject. If your advice is “don’t do that” I would love to hear alternatives.

Bill

PS: I have wondered in the past how an IRP passed to FsRtlCancellableWaitForSingleObject can be cancelled if there is no cancellation routine on it. Turns out that IopCancelIrpsInCurrentThreadListSpecialApc first sets Irp->Cancel and then alerts the thread with KeAlertThread. This wakes up the alertable KeWaitForSingleObject inside FsRtlCancellableWaitForSingleObject, which then checks Irp->Cancel. Mystery solved.

Perhaps you can set the IRP structure’s member IoStatus.Status = STATUS_ACCESS_DENIED; that the IRP will be cancelled?
I am not sure it’s ok. Hope the idea can help you.

@Cecilia_wll said:
Perhaps you can set the IRP structure’s member IoStatus.Status = STATUS_ACCESS_DENIED; that the IRP will be cancelled?
I am not sure it’s ok. Hope the idea can help you.

Thanks for your response.

My issue has to do more with whether it is a good idea to set the IRP_SYNCHRONOUS_API bit as I do not fully understand the implications of doing so. As I control both the FSD and the second driver that wishes to communicate with it, it is likely that my use is ok and I wanted to confirm this with the list.

In general, I’m not liking this whole design… which feels overly forced.

WHY do you need to support, specifically, the Win32 function CancelSynchrnousIo? Heck, until you mentioned it, I had never even HEARD of it.

Now, having SAID that… setting IRP_SYNCHRONOUS_API shouldn’t cause any problems. It’s merely a way to tell the file system that the request was issued from a “synchronous API” and the IRP must be processed synchronously (even if the FILE_OBJECT was not opened for synchronous access). This flag typically gets set for things like queries that are inherently synchronous, regardless of how the File Object was opened.

Peter

Peter, thanks for your answer.

WHY do you need to support, specifically, the Win32 function CancelSynchrnousIo? Heck, until you mentioned it, I had never even HEARD of it.

In general I agree with you. CancelSynchronousIo is not my favorite API as it requires care to make it work right.

In this case I am porting a user mode library from UNIX, which uses synchronous calls like read(2) and likes to pthread_cancel(3) a lot. On top of that my file system driver’s special FSCTL’s are all synchronous (they never return STATUS_PENDING) as they effectively implement a “GetNextQueueItem” from a queue.

For these reasons I chose to implement pthread_cancel(3) in terms of CancelSynchronousIo (and some other machinery). It was the path of least resistance in this case and I do not like to overcomplicate things when I do not have to.

setting IRP_SYNCHRONOUS_API shouldn’t cause any problems

Thanks for the confirmation. It does work indeed in my tests. But this is new software and my test suite is still small.

I think the combination of using IoBuildDeviceIoControlRequest (to get a threaded IRP) and then patching the IRP to change MJ/MN function, adding a FileObject and setting the IRP_SYNCHRONOUS_API flag made me uneasy. I would much prefer the IoAllocateIrp route, but IoQueueThreadIrp is marked as “reserved for system use”.

Bill

Hello,

ïf you wish to make an IRP cancel you need to set a cancellation routine for it (IoSetCancelRoutine). Basically, the routine is then invoked when the IRP is cancelled and you may complete it (usually with STATUS_CANCELLED).

You may do this only for IRPs that are owned by your driver. If your driver sends an IRP to another driver you should again set its canel routine to NULL, thus making it not cancellable again.