You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
**Wrapper / common-caller pattern.** The query reasons about IRQL-changing calls between save and restore not only when both calls share an enclosing function, but also when they sit inside one-level helper wrappers that are called from a common caller (for example, thin `save_fp_helper`/ `restore_fp_helper` functions that simply forward to `KeSaveFloatingPointState` / `KeRestoreFloatingPointState`). In those cases the intermediate IRQL transition is searched in the common caller (or in either helper's enclosing function for the asymmetric case where one side is a helper and the other is direct).
57
+
**Wrapper / common-caller pattern.** The query searches for IRQL-changing calls between save and restore in either their shared enclosing function, or — when one or both endpoints sit inside a thin one-level helper (e.g. `save_fp_helper`forwarding to `KeSaveFloatingPointState`) — in the common caller of those helpers.
58
58
59
-
**Remaining limitations.** Despite the source-position and AST-loop branches, the predicate still does not detect:
59
+
**Known false negatives:**
60
60
61
-
***IRQL changes performed deep inside helper bodies.** If the helper function itself raises or lowers the IRQL after the save (or before the restore), the change is not visible from the common caller's source-position view, and no intermediate IRQL change is found. Annotating the helper with `_IRQL_raises_`or`_IRQL_saves_global_`makes its IRQL behavior visible without body inspection.
62
-
***Indirect calls.** IRQL changes performed by an indirect call (function pointer or dispatch-table call) between save and restore are not detected, because the predicate only inspects the static call target.
63
-
***Loops where the restore is textually before the save.** The AST-loop branch of `irqlChangesBetween` correctly recognises that all three calls (save, restore, and an IRQL-changing call) sit inside the same loop body and would fire on this pattern. However, the upstream IRQL analysis library used to compute the IRQL at the save and restore sites does not consistently bind a value at the argument expression of `KeSaveFloatingPointState` when the call is inside a loop body, so the `irqlSource != irqlSink` filter rejects these cases before `irqlChangesBetween` is consulted. Recovering this true positive requires improvements to the IRQL analysis library and not just to this query.
64
-
***Wrapper chains longer than one level.** Only one level of wrapping is currently modelled (the helper is called directly from the common caller). Multi-level wrappers require the same annotation hint described above, or a direct call site.
61
+
***IRQL changes deep inside helper bodies.** If a helper raises/lowers IRQL between its entry and the save/restore primitive it forwards to, that change isn't visible from the common caller. Annotate the helper with `_IRQL_raises_`/`_IRQL_saves_global_`to make its IRQL behavior visible without body inspection.
62
+
***Indirect calls.** IRQL changes via function pointer or dispatch-table dispatch are not recognized; the predicate inspects only the static call target.
63
+
***Loops where restore is textually before save.** The AST-loop branch of `irqlChangesBetween` correctly recognizes such patterns, but the upstream IRQL cascade does not always bind at `KeSaveFloatingPointState`'s argument expression inside loop bodies, so the `irqlSource != irqlSink` filter rejects them before this predicate fires. Recovering this case needs work in `Irql.qll`.
64
+
***Wrapper chains longer than one level.** Only one level of helper wrapping is modelled. Multi-level wrappers need the annotation hint above, or a direct call site.
Copy file name to clipboardExpand all lines: src/drivers/general/queries/IrqlSetTooHigh/IrqlSetTooHigh.md
+1-3Lines changed: 1 addition & 3 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -28,7 +28,5 @@ This query may provide false positives in cases where functions are not annotate
28
28
29
29
For information on how to annotate your functions with information about how they adjust the IRQL, see "IRQL annotations for drivers" in the references section.
30
30
31
-
**Lower-on-exit pattern: known false negative.** When a function has no `_IRQL_always_function_max_` but does carry an `_IRQL_raises_(R)` annotation, the query treats `R` as the implicit ceiling for the function body. A function annotated as both `_IRQL_requires_min_(M)` and `_IRQL_raises_(R)` with `M > R` is interpreted as a "lower IRQL on exit" pattern (for example a wrapper around a mutex or spin-lock release that runs at `DISPATCH_LEVEL` on entry and returns at `PASSIVE_LEVEL`). For these functions the implicit ceiling is suppressed entirely, because `R` describes the exit IRQL rather than a maximum.
32
-
33
-
This means that a buggy "lower-on-exit" function whose body raises the IRQL above `M` at some intermediate point will *not* be flagged. If the function actually has a maximum that should be enforced along the body, declare it explicitly with `_IRQL_always_function_max_(MAX)` so the query has a concrete ceiling to check against.
31
+
**Lower-on-exit pattern: known false negative.** A function annotated `_IRQL_requires_min_(M)` + `_IRQL_raises_(R)` with `M > R` is treated as "raises only at exit" (e.g. a wrapper around a spin-lock or mutex release that enters at `DISPATCH_LEVEL` and returns at `PASSIVE_LEVEL`): the query suppresses the implicit ceiling so `R` is read as the exit IRQL rather than a body-wide maximum. Consequently a buggy lower-on-exit function whose body raises IRQL above `M` in the middle is not flagged. To enforce a body-wide maximum, declare it explicitly with `_IRQL_always_function_max_(MAX)`.
Copy file name to clipboardExpand all lines: src/drivers/general/queries/IrqlSetTooHigh/IrqlSetTooHigh.qhelp
+14-19Lines changed: 14 additions & 19 deletions
Original file line number
Diff line number
Diff line change
@@ -36,26 +36,21 @@
36
36
<p>This query uses interprocedural data-flow analysis and can take a large amount of CPU time and memory to run.</p>
37
37
<p>This query may provide false positives in cases where functions are not annotated with their expected IRQL ranges or behaviors.</p>
38
38
<p>For information on how to annotate your functions with information about how they adjust the IRQL, see "IRQL annotations for drivers" in the references section.</p>
39
+
<!-- === AI-generated === -->
39
40
<p>
40
-
<b>Lower-on-exit pattern: known false negative.</b> When a function has
41
-
no <code>_IRQL_always_function_max_</code> but does carry an
42
-
<code>_IRQL_raises_(R)</code> annotation, the query treats <code>R</code>
43
-
as the implicit ceiling for the function body. A function annotated as
44
-
both <code>_IRQL_requires_min_(M)</code> and
45
-
<code>_IRQL_raises_(R)</code> with <code>M > R</code> is interpreted
46
-
as a "lower IRQL on exit" pattern (for example a wrapper around a mutex
47
-
or spin-lock release that runs at <code>DISPATCH_LEVEL</code> on entry
48
-
and returns at <code>PASSIVE_LEVEL</code>). For these functions the
49
-
implicit ceiling is suppressed entirely, because <code>R</code> describes
50
-
the exit IRQL rather than a maximum.
51
-
</p>
52
-
<p>
53
-
This means that a buggy "lower-on-exit" function whose body raises the
54
-
IRQL above <code>M</code> at some intermediate point will <i>not</i> be
55
-
flagged. If the function actually has a maximum that should be enforced
56
-
along the body, declare it explicitly with
57
-
<code>_IRQL_always_function_max_(MAX)</code> so the query has a concrete
58
-
ceiling to check against.
41
+
<b>Lower-on-exit pattern: known false negative.</b> A function
42
+
annotated <code>_IRQL_requires_min_(M)</code> +
43
+
<code>_IRQL_raises_(R)</code> with <code>M > R</code> is
44
+
treated as "raises only at exit" (e.g. a wrapper around a
45
+
spin-lock or mutex release that enters at
46
+
<code>DISPATCH_LEVEL</code> and returns at
47
+
<code>PASSIVE_LEVEL</code>): the query suppresses the
48
+
implicit ceiling so <code>R</code> is read as the exit IRQL
49
+
rather than a body-wide maximum. Consequently a buggy
50
+
lower-on-exit function whose body raises IRQL above
51
+
<code>M</code> in the middle is not flagged. To enforce a
0 commit comments