How often should we check the IRQL? what is the best practice?

Hi Folks,

I have some questions regarding checking IRQL :

  1. Can my driverEntry, driverUnload, and my dispatch functions, be called at any IRQL? should i always assume the worst case scenario (highest IRQL) ? i always thought i should assume my driver code is at PASSIVE_LEVEL if i don’t raise it myself, but based on reading some posts here it seems like this is not true…

  2. If so, then i should always check the IRQL before accessing any address that could be paged out, calling any API that cannot be called if an IRQL is high, right? because if so, then i should be even checking the IRQL before every single DbgPrint!! because based on MSDN :

DbgPrint and DbgPrintEx can be called at IRQL<=DIRQL. However, Unicode format codes (%wc and %ws) can be used only at IRQL=PASSIVE_LEVEL

  1. And again, if i do in fact need to check every address (for example an address in my buffer that is allocated with paged flag) that I’m accessing to make sure its not paged out, what is the best practice for this? because again, based on reading some posts in this forum, MmIsAddressValid is not the proper way of doing it (because it could get paged out just after calling this function), so what should i do?

I got paranoid because after reading some of the posts here, it seems like it can never be assumed which IRQL any of my codes in my driver will be executed, therefore i should be checking the IRQL non stop?? because even if i check it in the start of every function, how can i be sure that it doesn’t get raised in the middle of my function (if for example a thread switch happens and another module raises it) !

If i do in fact need to check the IRQL before any API that cannot be called at a certain IRQL, then isn’t this too much headache? i haven’t seen a single driver code sample that does any check, even when calling these APIs that cannot be called at certain IRQL?

So sorry because of rookie question.

Can my driverEntry, driverUnload, and my dispatch functions, be called at any IRQL?

The former two are guaranteed to be called at PASSIVE_LEVEL (they are invoked in context of the system thread), while your dispatch functions (at least some of them) may be called at IRQL <= DISPATCH_LEVEL. More on it below

I got paranoid because after reading some of the posts here, it seems like it can never be assumed which IRQL any of my codes
in my driver will be executed.

As long as we are speaking about dispatch functions, the above statement, indeed, holds true, because they (at least some of them) may be invoked not only in context of a thread but also in the one of a DPC routine. If your driver handles interrupts (and,hence,queues DPCs), its ISR and DPC routine are guaranteed to be called at respectively DIRQL and DISPATCH_LEVEL. All initialization/cleanup routines like DrvEntry(),DrvUnload(),AddDevice() et al are going to be called in context of a system thread at PASSIVE_LEVEL

If so, then i should always check the IRQL before accessing any address that could be paged out, calling any API that cannot
be called if an IRQL is high, right?

Nope…

What you should do is just to structure your code in such way that a function that MAY be called at IRQL > PASSIVE_LEVEL never does anything that is not allowed at IRQL > PASSIVE_LEVEL; a function that MAY be called at DISPATCH_LEVEL never does anything that is allowed only at IRQL < DISPATCH_LEVEL;etc…

i haven’t seen a single driver code sample that does any check, even when calling these APIs that cannot be called at certain IRQL?

Now you already know why it works this way…

Anton Bassov

@anton_bassov said:

What you should do is just to structure your code in such way that a function that MAY be called at IRQL > PASSIVE_LEVEL never does anything that is not allowed at IRQL > PASSIVE_LEVEL; a function that MAY be called at DISPATCH_LEVEL never does anything that is allowed only at IRQL < DISPATCH_LEVEL;etc…

Hi Anton,

but in many scenarios it might be impossible to structure my code in such a way that in NEVER fails when IRQL is higher than PASSIVE_LEVEL,
For example as MSDN says, even DbgPrint might cause BSOD if your function is called at a high IRQL, if you are using stuff like %ws inside of it. i can’t just stop using DbgPrint in my codes!

or lets say i have allocated a pagable buffer, then as this forum suggests, even MmIsAddressValid might not be enough to check if its still in memory or not!

and these are just two examples, for example there might be many APIs like DbgPrint, that just like DbgPrint could cause BSOD if they are called at a high IRQL, that i don’t know about! I actually found out about this limitation of DbgPrint by accident when i was reading its MSDN page!

And as for driver samples out there, i have seen many many samples that never check the IRQL before using DbgPrint that has %ws/%wc inside of it, i literally have not seen one sample that does any check, so i assume since its so rare for dispatch functions to be called at a high IRQL (correct me if Im wrong), many people don’t face issues because of this, and therefore continue to not check the IRQL in their code, even when they should.

the dbgprint restriction is quite simple: don’t use wide string formats in code that can be run at >= DISPATCH_LEVEL. Each api, in general, clearly states its IRQL requirements. You are overthinking the problem, the solution is exactly as Anton stated.

You do not have to ‘check the irql’ if you understand what the irql is of your function. Again, for example, DriverEntry. You do have to consider IRQL for dispatch functions that an be called at both < and >= DISPATCH_LEVEL. With WDF you can push this back to the framework and require all your dispatch entry points to be at PASSIVE_LEVEL. If you have hardware, interrupt and dpc processing has to be coded appropriate to either DIRQL or DISPATCH_LEVEL requirements.

If you have allocated a pageable buffer you had better only be using the virtual address of that buffer in functions that only run at < DISPATCH_LEVEL. If you need to access the buffer at >= DISPATCH _LEVEL you need to lock it into memory using an MDL.

@Mark_Roddy said:
the dbgprint restriction is quite simple: don’t use wide string formats in code that can be run at >= DISPATCH_LEVEL. Each api, in general, clearly states its IRQL requirements. You are overthinking the problem, the solution is exactly as Anton stated.

But i have tons of DbgPrints already in my codes that use %ws, and i need to keep these DbgPrints, so the only solutions are to check the IRQL in the start of every dispatch function, OR convert every wide string to ascii string and then pass that ascii to DbgPrint, correct?

Also if i go with the IRQL checking path, do i need to check the IRQL before every DbgPrint that has %ws, or just checking it one time in the start of dispatch function is enough?

In the typical driver, the DISPATCH sections are usually fairly well contained. What are you doing that might have this problem?

@Tim_Roberts said:
In the typical driver, the DISPATCH sections are usually fairly well contained. What are you doing that might have this problem?

Sorry for rookie question but what do you mean by DISPATCH section? do you mean my dispatch functions? because when you say section i think of the sections in the PE structure.

I am trying to make sure my code does not cause any BSOD because of IRQL. just want to write a clean and standard code. so do expert driver developers ever check IRQL in their dispatch functions to make sure is correct, or they just write their code in such a way that there is no need to worry about IRQL? and is it possible that in the middle of my dispatch function IRQL get raised by another kernel module?

dispatch functions: the ‘top level’ callbacks into your driver, contrasted with interrupt and dpc callbacks, all of which run at >= DISPATCH_LEVEL.

IRQL is not going to change within your callbacks unless you change the IRQL, by for example acquiring a spinlock.

By DISPATCH, I mean those parts of the code that run at DISPATCH_LEVEL. There are many kinds of drivers that spend very little time above PASSIVE_LEVEL, and others that have to worry about it. Typically, you won’t be encountering UNICODE_STRINGs above PASSIVE_LEVEL.

@Tim_Roberts said:
By DISPATCH, I mean those parts of the code that run at DISPATCH_LEVEL.

But as i said, i never raise the IRQL myself ?

So to recap, assuming i never raise the IRQL myself, my driver startup and unload function always run at PASSIVE_LEVEL. but my major functions such as the one responsible IRP_MJ_DEVICE_CONTROL, could get called, at a higher IRQL than the PASSIVE_LEVEL. is this correct? if so, how common is this to happen, meaning a major function that i implemented for god knows why getting called at with a IRQL higher than PASSIVE_LEVEL? (again, assume that i never raise the IRQL myself in my code)

If your dispatch routines are being called from user-mode, they will be at PASSIVE_LEVEL. It is possible for a driver to call your dispatch routines after having raised the IRQL, but that would be in violation of your contract.

@Tim_Roberts said:
If your dispatch routines are being called from user-mode, they will be at PASSIVE_LEVEL. It is possible for a driver to call your dispatch routines after having raised the IRQL, but that would be in violation of your contract.

So if none of my major functions (this is what i mean by dispatch functions) are called by another driver, i am fine and there is no need to worry about IRQL being higher than PASSIVE_LEVEL when the major function is called, correct? because in my scenario, my major functions are intended for user mode communication, so i assume no other driver should be calling my major functions (why would they?)

With all due respect, this is a very scary thread.

You are writing kernel-mode code, but you don’t understand the concept of IRQLs. That’s not good, no matter how you look at it. That’s a bit like driving a car, but not understanding the concept of the brake pedal.

Peter

If you are writing a virtual driver that doesn’t have any hardware beneath you, then you will probably not encounter DISPATCH_LEVEL unless you need to grab a spinlock. Such a driver has few reasons to use a DPC. Drivers driving real hardware have many more conditions to worry about.

If you are using the driver framework you can require passive level for all your dispatch callbacks and stop worrying about it.

@“Peter_Viscarola_(OSR)” said:
With all due respect, this is a very scary thread.

You are writing kernel-mode code, but you don’t understand the concept of IRQLs. That’s not good, no matter how you look at it. That’s a bit like driving a car, but not understanding the concept of the brake pedal.

Peter

Sorry Peter, although i did mention in my question that its a rookie one but i apologize again, I am still learning… i would really appreciate if you can suggest a good article/source so i can completely learn everything i need about IRQLs in the context of windows kernel programming.

This is a quote from Windows kernel programming book:

The relevant dispatch routine (based on the major function code) is the first routine in a driver that sees the request. Normally, it’s called by the requesting thread context, i.e. the thread that called the relevant API (e.g. ReadFile) in IRQL PASSIVE_LEVEL (0). However, it’s possible that a filter driver sitting on top of this device sent the request down in a different context - it may be some other thread unrelated to the original requestor and even in higher IRQL, such as DISPATCH_LEVEL (2).

This is whats confusing me, why would a filter driver raise the IRQL but before reverting it, calls another driver? this seems to be something very rare too if i am not mistaken, because i myself have never encountered any BSOD in my drivers even tho i never assumed my dispatch routines could be called at a higher IRQL than passive…

@Tim_Roberts said:
If you are writing a virtual driver that doesn’t have any hardware beneath you, then you will probably not encounter DISPATCH_LEVEL unless you need to grab a spinlock.

Tim what are your thoughts on the quote from kernel programming book? so this means that the only way that the dispatch routine in my virtual driver gets called with a higher IRQL than passive is if a filter driver that is attached to the device raises the IRQL and “forgets” to lower it before calling my dispatch routine, correct? still doesn’t make any sense, because why would anyone raise the IRQL and call another driver before lowering it… the same book says that we should never do this and always revert the IRQL after we are done in the same function.

@Mark_Roddy said:
If you are using the driver framework you can require passive level for all your dispatch callbacks and stop worrying about it.

I have never used the driver framework, but how does it stop other drivers such as upper filter drivers from calling my dispatch routine after raising IRQL? also sorry for wasting your time by asking these rookie questions, as i said i am still learning and never claimed to be an expert in this area.

The filter (or any other kernel mode component sending the IRP to your driver) could be sending the IO request from a DPC. Another scenario, however unlikely and potentially incorrect, is that the caller is sending the IO while hold a spinlock. In both cases, sender of the IO is not forgetting to lower IRQL while you are processing the IO.

The framework queues a work item to defer execution to passive level in the case the IO arrives at IRQL > PASSIVE_LEVEL. The source for WDF is available on github, you can see for yourself how it is implemented.

I have never used the driver framework, but how does it stop other drivers such as upper filter drivers from calling my dispatch routine after raising IRQL?

The point is, you establish a contract that your dispatch routines are called as PASSIVE_LEVEL. If some moron filter driver send you an IRP at DISPATCH_LEVEL, then the BSOD is their fault. You could defend against it, but is it really worth the trouble? Any cooperative filter driver is going to comply.

but is it really worth the trouble?

WDF. PASSIVE_LEVEL Execution Constraint. Problem solved.

Peter