IRP_MJ_DIRECTORY_CONTROL at APC_LEVEL

According to Microsoft docs, http://msdn.microsoft.com/en-us/library/windows/hardware/ff540124(v=vs.85).aspx Directory Control dispatch routine can be called at APC_LEVEL. Under what circumstances will it be so?

The documentation for ZwQueryDirectoryFile states that the function must be called at PASSIVE_LEVEL, with special kernel APCs enabled. FltQueryDirectoryFile seems stricter as it states all APCs must be enabled.

If all callers of ZwQueryDirectoryFile and friends are calling at PASSIVE_LEVEL, how does the directory control dispatch end up at APC_LEVEL?

I am doing a IoCreateFileSpecifyDeviceObjectHint inside my directory control dispatch routine. This function must also be called at PASSIVE_LEVEL. So far, I am getting away with it, but I do not know when it will break.

Thanks.
aaron

Roll your own IRP_MJ_DIRECTORY_CONTROL at APC level.

Kind regards, Dejan.

On Sat, Jan 18, 2014 at 8:19 AM, wrote:

> According to Microsoft docs,
> http://msdn.microsoft.com/en-us/library/windows/hardware/ff540124(v=vs.85).aspxDirectory Control dispatch routine can be called at APC_LEVEL. Under what
> circumstances will it be so?
>
> The documentation for ZwQueryDirectoryFile states that the function must
> be called at PASSIVE_LEVEL, with special kernel APCs enabled.
> FltQueryDirectoryFile seems stricter as it states all APCs must be enabled.
>
> If all callers of ZwQueryDirectoryFile and friends are calling at
> PASSIVE_LEVEL, how does the directory control dispatch end up at APC_LEVEL?
>
> I am doing a IoCreateFileSpecifyDeviceObjectHint inside my directory
> control dispatch routine. This function must also be called at
> PASSIVE_LEVEL. So far, I am getting away with it, but I do not know when it
> will break.
>
> Thanks.
> aaron
>
> —
> NTFSD is sponsored by OSR
>
> OSR is hiring!! Info at http://www.osr.com/careers
>
> For our schedule of debugging and file system seminars visit:
> http://www.osr.com/seminars
>
> To unsubscribe, visit the List Server section of OSR Online at
> http://www.osronline.com/page.cfm?name=ListServer
>

Question is, who does that? Does Microsoft do it?

If IRP_MJ_DIRECTORY_CONTROL can be called at APC_LEVEL, why must ZwQueryDirectoryFile be called at PASSIVE_LEVEL?

Thanks
aaron

Because if you call it with APCs disabled and the highest level driver marks the IRP as pending and returns STATUS_PENDING it will never complete - because the completion logic queues an APC object (it’s in the IRP actually) to indicate the completion status and clean up the IRP, but because you have blocked special kernel APCs the APC sits on the queue and the thread never unblocks.

If you are a kernel driver and send an IRP_MJ_DIRECTORY_CONTROL/IRP_MN_QUERY_DIRECTORY at APC level it won’t matter because YOU have provided a completion routine - which gets invoked. There’s no APC involved. Thus, it won’t hang.

In fact, if you DO call ZwQueryDirectoryFile it will work as long as the highest level driver doesn’t mark the IRP as pending and return STATUS_PENDING. So this is a subtle bug that you might never notice during testing.

If you look at the FasFat code, in FatQueryDirectory (dirctrl.c), you can see at least one path where this might happen:

if (InitialQuery || RestartScan) {

if (!FatAcquireExclusiveFcb( IrpContext, Dcb )) {

DebugTrace(0, Dbg, “FatQueryDirectory -> Enqueue to Fsp\n”, 0);
Status = FatFsdPostRequest( IrpContext, Irp );
DebugTrace(-1, Dbg, “FatQueryDirectory -> %08lx\n”, Status);

return Status;
}
// …

FatFsdPostRequest will return STATUS_PENDING (see workqueue.c):

FatPrePostIrp( IrpContext, Irp );

FatAddToWorkque( IrpContext, Irp );

//
// And return to our caller
//

return STATUS_PENDING;

Note that it ALWAYS returns STATUS_PENDING.

Thus, this isn’t a theoretical issue - it really CAN happen. It’s an issue specific to the way the native API function is written however.

Tony
OSR

Thanks Tony, I understand now why Zw*** functions must be called at PASSIVE_LEVEL.

The MS docs http://msdn.microsoft.com/en-us/library/windows/hardware/ff540124(v=vs.85).aspx describe the IRQLs for the various dispatch routines. Are these hard requirements, meaning that the FSD expects us to send these IRPs at the appropriate IRQL? For those operations that say PASSIVE_LEVEL, does it also imply that the FSD may queue an APC on the thread, hence the requirement?

Or are the levels simply what IO Manager sends the IRP at? If we roll our own IRP, can we send it at a level higher than what is specified? Eg Send a IRP_MJ_CREATE at APC_LEVEL instead of PASSIVE.

Thanks for the help and patience
aaron

If you respect the IRQL restrictions listed, you won’t run into any problems. I think it’s being a bit conservative here, but I cannot blame them because it’s difficult to explain the “but if you roll your own IRP then you could do this with special kernel APCs disabled”.

None of these should be a burden for you, because nothing you do in a driver should be blocking APCs.

Tony
OSR

It is an issue for us because we wish to invoke a CreateFile during a IRP_MJ_DIRECTORY_CONTROL routine, but if the routine comes in at APC_LEVEL, theoretically we cannot invoke the create anymore. I thought we might get away with rolling my own Creates, but I suppose the right way will be to post the create to a worker thread, and possibly synchronize with an event, or return STATUS_PENDING as FastFat has done.

Thanks
aaron