Skip to content

Commit 9a8eba1

Browse files
committed
[AI] More IRQL correctness tweaks
1 parent 65e6328 commit 9a8eba1

3 files changed

Lines changed: 61 additions & 47 deletions

File tree

src/drivers/general/queries/IrqlFloatStateMismatch/IrqlFloatStateMismatch.ql

Lines changed: 0 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -42,39 +42,6 @@ module FloatStateFlowConfig implements DataFlow::ConfigSig {
4242

4343
module FloatStateFlow = DataFlow::Global<FloatStateFlowConfig>;
4444

45-
/**
46-
* Holds if `fc` is a call that may change the IRQL. This includes the
47-
* IRQL primitives (KeRaiseIrql, KeLowerIrql, KfRaiseIrql, KfLowerIrql,
48-
* etc.), functions annotated with _IRQL_raises_ or
49-
* _IRQL_saves_global_ / _IRQL_restores_global_, and functions whose
50-
* body itself transitively contains an IRQL-changing call (i.e.,
51-
* unannotated wrapper helpers).
52-
*
53-
* The transitive closure over the call graph is necessary to avoid
54-
* false negatives where a driver wraps the IRQL primitives in a helper
55-
* function without the appropriate SAL annotations.
56-
*/
57-
predicate isIrqlChangingFunction(Function f) {
58-
f instanceof IrqlChangesFunction
59-
or
60-
exists(FunctionCall inner |
61-
inner.getEnclosingFunction() = f and
62-
isIrqlChangingCall(inner)
63-
)
64-
}
65-
66-
predicate isIrqlChangingCall(FunctionCall fc) {
67-
fc instanceof KeRaiseIrqlCall
68-
or
69-
fc instanceof KeLowerIrqlCall
70-
or
71-
fc instanceof RestoresGlobalIrqlCall
72-
or
73-
fc instanceof SavesGlobalIrqlCall
74-
or
75-
isIrqlChangingFunction(fc.getTarget())
76-
}
77-
7845
/**
7946
* Gets a source line in `f` that anchors `fc` from `f`'s perspective:
8047
*

src/drivers/general/queries/IrqlInconsistentWithRequired/IrqlInconsistentWithRequired.ql

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,14 @@ where
3030
entryCfn = f.getBlock() and
3131
irqlLevelEntry = getPotentialExitIrqlAtCfn(entryCfn) and
3232
irqlLevelExit = getPotentialExitIrqlAtCfn(exitCfn) and
33-
irqlLevelEntry != irqlLevelExit
33+
irqlLevelEntry != irqlLevelExit and
34+
// Soundness filter: only flag if `f` actually contains an IRQL-changing
35+
// call (directly or transitively through a wrapper). This is necessary
36+
// due to our IRQL CFG analysis producing ranges of potential IRQLs.
37+
exists(FunctionCall fc |
38+
fc.getEnclosingFunction() = f and
39+
isIrqlChangingCall(fc)
40+
)
3441
select f,
3542
"Possible IRQL level at function completion inconsistent with the required IRQL level for some path. Irql level expected: "
3643
+ irqlLevelEntry + ". Irql level found: " + irqlLevelExit +

src/drivers/libraries/Irql.qll

Lines changed: 53 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -534,7 +534,7 @@
534534
* "the IRQL before the corresponding save global call."
535535
*/
536536
int getIrqlLevel() {
537-
result = any(getPotentialExitIrqlAtCfnRaw(this.getMostRecentRaise().getAPredecessor()))
537+
result = any(getPotentialExitIrqlAtCfnInternal(this.getMostRecentRaise().getAPredecessor()))
538538
}
539539

540540
int getIrqlLevelExplicit() {
@@ -614,7 +614,7 @@
614614
*/
615615
int getIrqlLevel() {
616616
result =
617-
any(getPotentialExitIrqlAtCfnRaw(this.getMostRecentRaiseInterprocedural().getAPredecessor()))
617+
any(getPotentialExitIrqlAtCfnInternal(this.getMostRecentRaiseInterprocedural().getAPredecessor()))
618618
}
619619

620620
int getIrqlLevelExplicit() {
@@ -665,7 +665,7 @@
665665
* "the IRQL before the corresponding save global call."
666666
*/
667667
int getIrqlLevel() {
668-
result = any(getPotentialExitIrqlAtCfnRaw(this.getMostRecentRaise().getAPredecessor()))
668+
result = any(getPotentialExitIrqlAtCfnInternal(this.getMostRecentRaise().getAPredecessor()))
669669
}
670670

671671
int getIrqlLevelExplicit() {
@@ -723,7 +723,7 @@
723723
)
724724
or
725725
not exists(Expr child | child = e1.getAChild() or child = e2.getAChild())
726-
}
726+
}
727727

728728
/** Utility function to get all exit points of a function. */
729729
pragma[nomagic]
@@ -745,7 +745,7 @@
745745
*/
746746
pragma[nomagic]
747747
private int functionExitIrql(Function f) {
748-
result = getPotentialExitIrqlAtCfnRaw(getAnExitPointOfFunction(f))
748+
result = getPotentialExitIrqlAtCfnInternal(getAnExitPointOfFunction(f))
749749
}
750750

751751
/**
@@ -790,9 +790,9 @@
790790
* Not implemented: _IRQL_limited_to_
791791
*/
792792
int getPotentialExitIrqlAtCfn(ControlFlowNode cfn) {
793-
result = getPotentialExitIrqlAtCfnRaw(cfn)
793+
result = getPotentialExitIrqlAtCfnInternal(cfn)
794794
or
795-
not exists(getPotentialExitIrqlAtCfnRaw(cfn)) and
795+
not exists(getPotentialExitIrqlAtCfnInternal(cfn)) and
796796
result = astLevelExitIrqlFallback(cfn)
797797
}
798798

@@ -803,7 +803,7 @@
803803
* `getPotentialExitIrqlAtCfn` instead, which additionally consults
804804
* `astLevelExitIrqlFallback` when this cascade yields no value.
805805
*/
806-
private int getPotentialExitIrqlAtCfnRaw(ControlFlowNode cfn) {
806+
private int getPotentialExitIrqlAtCfnInternal(ControlFlowNode cfn) {
807807
if cfn instanceof KeRaiseIrqlCall
808808
then result = cfn.(KeRaiseIrqlCall).getIrqlLevel()
809809
else
@@ -824,18 +824,18 @@
824824
if
825825
cfn instanceof FunctionCall and
826826
cfn.(FunctionCall).getTarget() instanceof IrqlRequiresSameAnnotatedFunction
827-
then result = getPotentialExitIrqlAtCfnRaw(cfn.getAPredecessor())
827+
then result = getPotentialExitIrqlAtCfnInternal(cfn.getAPredecessor())
828828
else
829829
if cfn instanceof FunctionCall
830830
then result = functionExitIrql(cfn.(FunctionCall).getTarget())
831831
else
832832
if exists(ControlFlowNode cfn2 | cfn2 = cfn.getAPredecessor())
833-
then result = getPotentialExitIrqlAtCfnRaw(cfn.getAPredecessor())
833+
then result = getPotentialExitIrqlAtCfnInternal(cfn.getAPredecessor())
834834
else
835835
if exists(callerPredecessor(cfn.getControlFlowScope()))
836836
then
837837
result =
838-
getPotentialExitIrqlAtCfnRaw(callerPredecessor(cfn.getControlFlowScope()))
838+
getPotentialExitIrqlAtCfnInternal(callerPredecessor(cfn.getControlFlowScope()))
839839
else
840840
if
841841
cfn.getControlFlowScope() instanceof IrqlRestrictsFunction and
@@ -878,7 +878,7 @@
878878
closer.getLocation().getStartLine() < cfnStmt.getLocation().getStartLine() and
879879
closer.getLocation().getStartLine() > prev.getLocation().getStartLine()
880880
) and
881-
result = getPotentialExitIrqlAtCfnRaw(prev)
881+
result = getPotentialExitIrqlAtCfnInternal(prev)
882882
)
883883
}
884884

@@ -1060,4 +1060,44 @@
10601060
)
10611061
)
10621062
)
1063-
}
1063+
}
1064+
/**
1065+
* Holds if `fc` is a call that may change the IRQL. This includes the
1066+
* IRQL primitives (KeRaiseIrql, KeLowerIrql, KfRaiseIrql, KfLowerIrql,
1067+
* etc., recognized via the `Ke*Irql*Call` and `*GlobalIrqlCall`
1068+
* classes), functions annotated with `_IRQL_raises_` or
1069+
* `_IRQL_saves_global_` / `_IRQL_restores_global_`, and functions
1070+
* whose body itself transitively contains an IRQL-changing call (i.e.,
1071+
* unannotated wrapper helpers).
1072+
*
1073+
* The transitive closure over the call graph is necessary to avoid
1074+
* false negatives where a driver wraps the IRQL primitives in a helper
1075+
* function without the appropriate SAL annotations.
1076+
*
1077+
* Lifted from `IrqlFloatStateMismatch.ql` so other queries (e.g.
1078+
* `IrqlInconsistentWithRequired`) can use the same soundness check.
1079+
*/
1080+
predicate isIrqlChangingCall(FunctionCall fc) {
1081+
fc instanceof KeRaiseIrqlCall
1082+
or
1083+
fc instanceof KeLowerIrqlCall
1084+
or
1085+
fc instanceof RestoresGlobalIrqlCall
1086+
or
1087+
fc instanceof SavesGlobalIrqlCall
1088+
or
1089+
isIrqlChangingFunction(fc.getTarget())
1090+
}
1091+
1092+
/**
1093+
* Holds if `f` directly or transitively contains an IRQL-changing call.
1094+
* See `isIrqlChangingCall`.
1095+
*/
1096+
predicate isIrqlChangingFunction(Function f) {
1097+
f instanceof IrqlChangesFunction
1098+
or
1099+
exists(FunctionCall inner |
1100+
inner.getEnclosingFunction() = f and
1101+
isIrqlChangingCall(inner)
1102+
)
1103+
}

0 commit comments

Comments
 (0)