James, did you try that on an x64 system? I expect Verifier to catch this kind of problem on x86, but not on x64.
Unfortunately, Verifier IRQL Checking is indeed less effective on x64, compared with x86, because for example KeRaiseIrql is inlined. Verifier doesn’t have (yet?) a way to hook these inline function calls, and therefore these calls don’t get verified. Here’s the entire implementation of KeRaiseIrql/KfRaiseIrql, from my copy of wdm.h:
KIRQL OldIrql;
OldIrql = KeGetCurrentIrql();
NT_ASSERT(OldIrql <= NewIrql);
WriteCR8(NewIrql);
return OldIrql;
So you would have to build a checked driver to catch this bug, using the assertion above.
BTW, another example of x64 Verifier being less useful than the x86 version is: until recently Verifier had no way to capture stack traces at DISPATCH_LEVEL or above. Therefore, the information displayed by some of the !verifier commands on x64 was not as useful as on x86. Capturing stack traces at DISPATCH_LEVEL on x64 works pretty well starting with Windows 7 though.
Thanks,
Dan
-----Original Message-----
From: xxxxx@lists.osr.com [mailto:xxxxx@lists.osr.com] On Behalf Of James Harper
Sent: Friday, August 14, 2009 4:25 PM
To: Windows System Software Devs Interest List
Subject: RE: [ntdev] function IRQL dependency
> The only problem with this approach is that it “breaks the
contract” that a
thread running at >DISPATCH_LEVEL cannot be preempted
Elevated IRQL is just a Windows notion of atomic context - by raising
IRQL
>=DPC level you tell the dispatcher “please don’t perform context
switches on
this CPU because doing so may have disastrous consequences, until I
indicate
that it can get safely done”. Consider what can happen if you hold a
spinlock
or run DPC routine and some other thread is schedules on CPU
meanwhile.
Dispatcher has no means of knowing that performing context switch at
the
moment is unsafe, does it. This is what the concept of elevated IRQL
is all
about - to let dispatcher know that it should not perform context
switches
at the moment. This is just a convention that system dispatcher is
build
around.
Although , as we can see, Windows does not seem to enforce this
“contract” in
some situations, it is obviously just a bug that they will (hopefully)
fix…
If the contract says you can’t do it, and you do it anyway, is the
resulting behaviour really a bug? This is kernel space not user space,
so it is up to the caller to check the inputs, not the callee.
That said, the docs for KeRaiseIrql say “If the new IRQL is less than
the current IRQL, a bug check occurs”, and it clearly doesn’t under 2K3,
even when the verifier is running (just tried it [1]). If the first line
in KeDelayExecutionThread was KeRaiseIrql(APC_LEVEL, &old_irql) and
KeDelayExecutionThread was called at HIGH_LEVEL, the the current IRQL
would be changed to APC_LEVEL and old_irql = HIGH_LEVEL without any fuss
at all (aside from the obvious 
[1] The code I tested this with, which starts at APC_LEVEL is:
KIRQL old_irql1, old_irql2;
KdPrint((" A Irql = %d\n", KeGetCurrentIrql()));
KeRaiseIrql(HIGH_LEVEL, &old_irql1);
KdPrint((" B Irql = %d\n", KeGetCurrentIrql()));
KeRaiseIrql(PASSIVE_LEVEL, &old_irql2);
KdPrint((" C Irql = %d\n", KeGetCurrentIrql()));
KeLowerIrql(old_irql2);
KdPrint((" D Irql = %d\n", KeGetCurrentIrql()));
KeLowerIrql(old_irql1);
KdPrint((" E Irql = %d\n", KeGetCurrentIrql()));
And the output was:
A Irql = 1
B Irql = 31
C Irql = 0
D Irql = 31
E Irql = 1
James
NTDEV is sponsored by OSR
For our schedule of WDF, WDM, debugging and other 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