RtlCompareUnicodeString at APC level

Hi All,

My requirement is to compare two strings ignoring the case at IRQL <= APC_LEVEL. MSDN documents that RtlCompareUnicodeString can only be called at PASSIVE_LEVEL. But I am not sure why is this restriction specially when RtlUpcaseUnicodeString can be called at IRQL <= APC_LEVEL. Do anyone have answer for this?

Thanks & Regards,
Amit Kulkarni.

I believe it can be used at APC_LEVEL as it depends only on pageable memory access.

At APC_LEVEL, you can queue a work item and wait for an event to be signaled when it is done. You cannot use (in general) user buffers in that case so the buffers must be mapped in system space before the work item is queued.

You can also rewrite the API. This should not be difficult if you do not need to rewrite the whole string API.

> But I am not sure why is this restriction specially when RtlUpcaseUnicodeString can be

called at IRQL <= APC_LEVEL. Do anyone have answer for this?

The very first thing that gets into one’s head is that this function’s code is pageable.

APC_LEVEL is normally used in FSDs and FSFs. If code runs at this level it means that it may be running at the paging IO path.

Now consider what happens if a code on paging IO path to the pagefile causes page fault on code.
In order to make it run a page fault has to be resolved. However, in order to resolve a page fault the system has to execute above mention code. Impasse…

This is why documentation says this function cannot be called at APC_LEVEL. This statement applies only to the code that may run on the paging IO path, i.e. to FSDs and FSFs. Unless your driver happens to fall into this category (or may get called by FSDs and FSFs), I believe you can safely ignore this restriction and call this function at any level < DISPATCH one…

Anton Bassov

The code on pagefile path IO should not generate pagefaults at PASSIVE_LEVEL as well.

The equally likely it might run at PASSIVE_LEVEL. Depends on IRQL when exception happened.

If a driver wants to know about being on pagefile / hiberfile / dumpfile IO path it should process IRP_MN_DEVICE_USAGE_NOTIFICATION for DeviceUsageTypePaging / DeviceUsageTypeHibernation / DeviceUsageTypeDumpFile or use common sense.

RtlCompareUnicodeString is pageable code. Windows resolves page faults at APC_LEVEL. It uses I/O completion ports which is internally done her job through APC mechanism. So, that’s why we cannot call it when IRQL > PASSIVE_LEVEL.

[quote]
Windows resolves page faults at APC_LEVEL.
[/quote] >

Actually it does this at IRQL at which fault happened. Faults happen at PASSIVE_LEVEL, APC_LEVEL and even DISPATCH_LEVEL when a PDT entry is invalid and should be fixed by page fault handler.

It uses I/O completion ports which is internally done her job through APC mechanism.

APC is not used for pagefaults processing. APC is used to complete non-paging IRP in a context of a user process to copy data to a user space. APC is a reason paging read happens at APC_LEVEL but not a mechanism to complete paging IRP. That is why APC is not used for paging read IRP completion.


Actually it does this at IRQL at which fault happened. Faults happen at
PASSIVE_LEVEL, APC_LEVEL and even DISPATCH_LEVEL when a PDT entry is invalid and
should be fixed by page fault handler.

[/quote]


I’ll check it again from WI. Maybe I’m not remembering correctly, my bad.

RtlCompareUnicodeString is safe at APC_LEVEL. Ultimately the only things you
can’t do at APC_LEVEL are things that rely on APCs (the most common example
being the ZwXxx APIs). The worst thing that will happen while calling
RtlCompareUnicodeString is that you’ll get a page fault and page fault
processing does not rely on APCs.

Random other things to add that have come up in this thread:

  1. You can’t touch anything pageable while processing a paging file I/O. So,
    it would be illegal to call this API in the context of a paging file I/O
    operation (but note that this is different than a non-paging file paging I/O
    operation, in which case this API would be valid).

  2. Prior to Server 2003 the Memory Manager would send paging I/O operations
    at APC_LEVEL. Server 2003 introduced the Guarded Mutex, which gives you the
    effect of APC_LEVEL (i.e. no APCs) but without changing the IRQL. The result
    is that if you check the IRQL you’ll see PASSIVE_LEVEL, but you still can’t
    do anything that relies on APCs (e.g. call a Zw API). You can check for the
    new behavior with KeAreAllApcsDisabled.

-scott
OSR
@OSRDrivers

wrote in message news:xxxxx@ntdev…


Actually it does this at IRQL at which fault happened. Faults happen at
PASSIVE_LEVEL, APC_LEVEL and even DISPATCH_LEVEL when a PDT entry is invalid
and
should be fixed by page fault handler.

[/quote]


I’ll check it again from WI. Maybe I’m not remembering correctly, my bad.

> Actually it does this at IRQL at which fault happened.

Originally, yes.However, don’t forget that before paging IO IRP gets sent to FSD IO Manager has to invoke FSD callbacks that acquire all the necessary locks in advance, and these locks happen to be of FAST_MUTEX and ERESOURCE type. Acquiring these locks,IIRC, raises IRQL to APC level.

Therefore, by the time paging IO reaches FSD IRQL is already going to be APC_LEVEL

OTOH, I haven’t had any practical experience with it( and haven’t seen Windows screen either) for the last 10 years, so that there is always a chance that I just forgot something, so that my statements on this topic should be taken with a grain of salt

In any case, this discuss on seems to be more appropriate for NTFSD, rather than NTDEV…

Anton Bassov

Only the FAST_MUTEX raises to APC_LEVEL, ERESOURCEs simply enter a critical
region. The fast mutex in the FCB header is not held across I/O operations.

I’m reminded of the immortal words of Coach Belichick (spoken about a week
ago, but things turn immortal quick around here):

“I would just say in general, it’s hard for a player who’s not playing
football to practice playing football…Like if that was the best way to do
it, to just not do anything for four weeks and go do pushups, I think then
maybe that’s what we’d do. But I honestly don’t think that’s the right way
to go.”

-scott
OSR
@OSRDrivers

wrote in message news:xxxxx@ntdev…

Actually it does this at IRQL at which fault happened.

Originally, yes.However, don’t forget that before paging IO IRP gets sent to
FSD IO Manager has to invoke FSD callbacks that acquire all the necessary
locks in advance, and these locks happen to be of FAST_MUTEX and ERESOURCE
type. Acquiring these locks,IIRC, raises IRQL to APC level.

Therefore, by the time paging IO reaches FSD IRQL is already going to be
APC_LEVEL

OTOH, I haven’t had any practical experience with it( and haven’t seen
Windows screen either) for the last 10 years, so that there is always a
chance that I just forgot something, so that my statements on this topic
should be taken with a grain of salt

In any case, this discuss on seems to be more appropriate for NTFSD, rather
than NTDEV…

Anton Bassov

Thanks to all for the help. So it seems that RtlCompareUnicodeString is safe at APC_LEVEL provided that execution is not in paging IO path.

FYI my driver is FS Minifilter driver and I am processing post IRP_MN_QUERY_DIRECTORY. I have used FltDoCompletionProcessingWhenSafe to ensure my code gets executed at IRQL <= APC_LEVEL. Where I am using RtlCompareUnicodeString to compare file names.

So I am assuming I am safe.

Small but important correction: “execution is not in paging *file* I/O path”
(i.e. FsRtlIsPagingFile returns TRUE).

Your usage is indeed safe.

-scott
OSR
@OSRDrivers

wrote in message news:xxxxx@ntdev…

Thanks to all for the help. So it seems that RtlCompareUnicodeString is safe
at APC_LEVEL provided that execution is not in paging IO path.

FYI my driver is FS Minifilter driver and I am processing post
IRP_MN_QUERY_DIRECTORY. I have used FltDoCompletionProcessingWhenSafe to
ensure my code gets executed at IRQL <= APC_LEVEL. Where I am using
RtlCompareUnicodeString to compare file names.

So I am assuming I am safe.