diff --git a/src/drivers/general/queries/IrqlFloatStateMismatch/IrqlFloatStateMismatch.md b/src/drivers/general/queries/IrqlFloatStateMismatch/IrqlFloatStateMismatch.md index eb7bd233..187e787c 100644 --- a/src/drivers/general/queries/IrqlFloatStateMismatch/IrqlFloatStateMismatch.md +++ b/src/drivers/general/queries/IrqlFloatStateMismatch/IrqlFloatStateMismatch.md @@ -52,3 +52,13 @@ Correct example ## References * [ C28111 ](https://learn.microsoft.com/en-us/windows-hardware/drivers/devtest/28111-floating-point-irql-mismatch) + +## Semmle-specific notes +**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. + +**Known false negatives:** + +* **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. +* **Indirect calls.** IRQL changes via function pointer or dispatch-table dispatch are not recognized; the predicate inspects only the static call target. +* **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`. +* **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. diff --git a/src/drivers/general/queries/IrqlFloatStateMismatch/IrqlFloatStateMismatch.qhelp b/src/drivers/general/queries/IrqlFloatStateMismatch/IrqlFloatStateMismatch.qhelp index b3e1b967..362074ae 100644 --- a/src/drivers/general/queries/IrqlFloatStateMismatch/IrqlFloatStateMismatch.qhelp +++ b/src/drivers/general/queries/IrqlFloatStateMismatch/IrqlFloatStateMismatch.qhelp @@ -60,4 +60,51 @@ + + +

+ 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. +

+

+ Known false negatives: +

+ +
diff --git a/src/drivers/general/queries/IrqlFloatStateMismatch/IrqlFloatStateMismatch.ql b/src/drivers/general/queries/IrqlFloatStateMismatch/IrqlFloatStateMismatch.ql index eb7ace37..1ef306dc 100644 --- a/src/drivers/general/queries/IrqlFloatStateMismatch/IrqlFloatStateMismatch.ql +++ b/src/drivers/general/queries/IrqlFloatStateMismatch/IrqlFloatStateMismatch.ql @@ -17,7 +17,7 @@ * @tags correctness * ca_ported * @scope domainspecific - * @query-version v1 + * @query-version v6 */ import cpp @@ -42,12 +42,139 @@ module FloatStateFlowConfig implements DataFlow::ConfigSig { module FloatStateFlow = DataFlow::Global; -from DataFlow::Node source, DataFlow::Node sink, int irqlSink, int irqlSource +/** + * --- AI-generated --- + * + * Gets a source line in `f` that anchors `fc` from `f`'s perspective: + * + * - If `fc`'s enclosing function is `f`, the anchor is `fc`'s own + * start line. + * - Otherwise, if `f` contains a call site whose static target is + * `fc`'s enclosing function, the anchor is that call site's start + * line. (One-level wrapper case.) + * + * This lets `irqlChangesBetween` reason about the relative source + * position of the save and the restore in any function that either + * directly contains the call or calls the helper that does. + */ +private int anchorLineForCall(Function f, FunctionCall fc) { + f = fc.getEnclosingFunction() and + result = fc.getLocation().getStartLine() + or + exists(FunctionCall site | + site.getEnclosingFunction() = f and + site.getTarget() = fc.getEnclosingFunction() and + f != fc.getEnclosingFunction() and + result = site.getLocation().getStartLine() + ) +} + +/** + * --- AI-generated --- + * + * Holds if some IRQL-changing call could run between `saveCall` and + * `restoreCall` at runtime. Used as a sanity filter on top of the + * dataflow result: dataflow already paired save -> restore, but + * without this filter we'd flag pairs whose hypothetical entry IRQLs + * differ even though no actual IRQL transition runs between them. + * + * Two additive disjuncts: + * + * 1. **Source-position branch.** A `FunctionCall` `mid` syntactically + * inside some `f` has `isIrqlChangingCall(mid)` and lies on a + * source line bracketed by `anchorLineForCall(f, save/restoreCall)`. + * Two cross-function reach mechanisms with different depth bounds + * cooperate: `anchorLineForCall` walks at most one call-graph edge + * (we need a concrete line in `f` to anchor each endpoint), but + * `isIrqlChangingCall` is transitively recursive through + * `isIrqlChangingFunction`, so `mid`'s target can chain through any + * number of wrappers before reaching a primitive (`KeRaiseIrql`, + * `_IRQL_raises_`, etc.). + * + * 2. **AST-loop branch.** All three calls share an enclosing loop body + * in the same function. The back-edge makes any IRQL-changing call + * in the loop a real transition between save and restore on a + * subsequent iteration, including when restore is textually above + * save (which makes branch 1's range empty). + * + * We use source-line bracketing rather than CFG reachability because + * the extracted cpp CFG can drop forward edges across `if (call(...))` + * and similar boundaries, silently losing TPs. The AST relation in + * branch 2 is densely populated and avoids that gap. + * + * Caveat: `getPotentialExitIrqlAtCfn` doesn't always bind at save-call + * argument expressions inside loop bodies in current extracted DBs, so + * branch 2 can fire correctly while the upstream `irqlSource != irqlSink` + * still filters it out. Recovering those needs work in `Irql.qll`. + * + * Performance: `pragma[inline_late]` + `bindingset[saveCall, restoreCall]` + * specialize this per call site after dataflow has bound the endpoints, + * turning a codebase-wide enumeration of (save, restore) pairs into a + * per-pair check. Both annotations are planner hints; semantics unchanged. + * + * --- Human comments --- + * + * Branch (1) wound up like this for perf reasons as well; a transitive + * check across all of the helper's internals gets expensive and in practice + * if there are helper functions involved they're pretty shallow. + */ +bindingset[saveCall, restoreCall] +pragma[inline_late] +predicate irqlChangesBetween(FunctionCall saveCall, FunctionCall restoreCall) { + // Branch 1: source-line bracketing in a function `f` that anchors + // both calls (directly enclosing or one-level wrapper / common caller). + exists(Function f, int saveLine, int restoreLine, FunctionCall mid | + saveLine = anchorLineForCall(f, saveCall) and + restoreLine = anchorLineForCall(f, restoreCall) and + mid.getEnclosingFunction() = f and + isIrqlChangingCall(mid) and + mid.getLocation().getStartLine() >= saveLine and + mid.getLocation().getStartLine() <= restoreLine and + mid != saveCall and + mid != restoreCall + ) + or + // Branch 2: all three calls live inside the body of the same loop in + // their shared enclosing function. The loop back-edge makes any + // IRQL-changing call in the body a real transition between save and + // restore on a subsequent iteration, even when source-line position + // would put restore before save. + exists(Function f, Loop l, FunctionCall mid | + f = saveCall.getEnclosingFunction() and + f = restoreCall.getEnclosingFunction() and + f = mid.getEnclosingFunction() and + isIrqlChangingCall(mid) and + mid != saveCall and + mid != restoreCall and + l.getStmt().getAChild*() = saveCall.getEnclosingStmt() and + l.getStmt().getAChild*() = restoreCall.getEnclosingStmt() and + l.getStmt().getAChild*() = mid.getEnclosingStmt() + ) +} + +from + DataFlow::Node source, DataFlow::Node sink, int irqlSink, int irqlSource, + FunctionCall saveCall, FunctionCall restoreCall where FloatStateFlow::flow(source, sink) and + // FloatStateFlow's source/sink predicates already restrict the flow's + // endpoints to be the first arguments of KeSaveFloatingPointState / + // KeRestoreFloatingPointState. The two arg(0) equalities below + // uniquely bind `saveCall` and `restoreCall` (each Expr has exactly + // one parent FunctionCall), without re-stating the function-name + // constraints. + source.asIndirectExpr() = saveCall.getArgument(0) and + sink.asIndirectExpr() = restoreCall.getArgument(0) and irqlSource = getPotentialExitIrqlAtCfn(source.asIndirectExpr()) and irqlSink = getPotentialExitIrqlAtCfn(sink.asIndirectExpr()) and - irqlSink != irqlSource + irqlSink != irqlSource and + // Only flag if there is an actual IRQL-changing call between the save + // and the restore (in source order), in either the directly enclosing + // function or a one-level wrapper / common-caller. If no IRQL-changing + // call exists between them, the IRQL is invariant within a single + // invocation and the mismatch is a may-analysis artifact from + // different hypothetical entry IRQLs. + irqlChangesBetween(saveCall, restoreCall) select sink.asIndirectExpr(), "The irql level where the floating-point state was saved (" + irqlSource + ") does not match the irql level for the restore operation (" + irqlSink + ")." diff --git a/src/drivers/general/queries/IrqlFloatStateMismatch/IrqlFloatStateMismatch.sarif b/src/drivers/general/queries/IrqlFloatStateMismatch/IrqlFloatStateMismatch.sarif index 9379bb24..b2bf15de 100644 --- a/src/drivers/general/queries/IrqlFloatStateMismatch/IrqlFloatStateMismatch.sarif +++ b/src/drivers/general/queries/IrqlFloatStateMismatch/IrqlFloatStateMismatch.sarif @@ -2,342 +2,576 @@ "$schema": "https://json.schemastore.org/sarif-2.1.0.json", "version": "2.1.0", "runs": [ - { - "tool": { - "driver": { - "name": "CodeQL", - "organization": "GitHub", - "semanticVersion": "2.19.3", - "notifications": [ - { - "id": "cpp/baseline/expected-extracted-files", - "name": "cpp/baseline/expected-extracted-files", - "shortDescription": { - "text": "Expected extracted files" - }, - "fullDescription": { - "text": "Files appearing in the source archive that are expected to be extracted." - }, - "defaultConfiguration": { - "enabled": true - }, - "properties": { - "tags": [ - "expected-extracted-files", - "telemetry" - ] - } - }, - { - "id": "cpp/extractor/summary", - "name": "cpp/extractor/summary", - "shortDescription": { - "text": "C++ extractor telemetry" - }, - "fullDescription": { - "text": "C++ extractor telemetry" - }, - "defaultConfiguration": { - "enabled": true - } - } - ], - "rules": [ - { - "id": "cpp/drivers/irql-float-state-mismatch", - "name": "cpp/drivers/irql-float-state-mismatch", - "shortDescription": { - "text": "Irql Float State Mismatch" - }, - "fullDescription": { - "text": "The IRQL where the floating-point state was saved does not match the current IRQL (for this restore operation)." - }, - "defaultConfiguration": { - "enabled": true, - "level": "warning" - }, - "properties": { - "tags": [ - "correctness" - ], - "description": "The IRQL where the floating-point state was saved does not match the current IRQL (for this restore operation).", - "feature.area": "Multiple", - "id": "cpp/drivers/irql-float-state-mismatch", - "impact": "Insecure Coding Practice", - "kind": "problem", - "name": "Irql Float State Mismatch", - "opaqueid": "CQLD-C28111", - "owner.email:": "sdat@microsoft.com", - "platform": "Desktop", - "precision": "medium", - "problem.severity": "warning", - "query-version": "v1", - "repro.text": "The IRQL at which the driver is executing when it restores a floating-point state is different than the IRQL at which it was executing when it saved the floating-point state.\n Because the IRQL at which the driver runs determines how the floating-point state is saved, the driver must be executing at the same IRQL when it calls the functions to save and to restore the floating-point state.", - "scope": "domainspecific" - } - } + { + "tool": { + "driver": { + "name": "CodeQL", + "organization": "GitHub", + "semanticVersion": "2.24.2", + "notifications": [ + { + "id": "cpp/baseline/expected-extracted-files", + "name": "cpp/baseline/expected-extracted-files", + "shortDescription": { + "text": "Expected extracted files" + }, + "fullDescription": { + "text": "Files appearing in the source archive that are expected to be extracted." + }, + "defaultConfiguration": { + "enabled": true + }, + "properties": { + "tags": [ + "expected-extracted-files", + "telemetry" + ] + } + }, + { + "id": "cli/file-coverage-baseline", + "name": "cli/file-coverage-baseline", + "shortDescription": { + "text": "File coverage baseline telemetry" + }, + "fullDescription": { + "text": "File coverage baseline telemetry" + }, + "defaultConfiguration": { + "enabled": true + } + }, + { + "id": "cli/platform", + "name": "cli/platform", + "shortDescription": { + "text": "Platform" + }, + "fullDescription": { + "text": "Platform" + }, + "defaultConfiguration": { + "enabled": true + } + }, + { + "id": "cpp/extractor/summary", + "name": "cpp/extractor/summary", + "shortDescription": { + "text": "C++ extractor telemetry" + }, + "fullDescription": { + "text": "C++ extractor telemetry" + }, + "defaultConfiguration": { + "enabled": true + } + } + ], + "rules": [ + { + "id": "cpp/drivers/irql-float-state-mismatch", + "name": "cpp/drivers/irql-float-state-mismatch", + "shortDescription": { + "text": "Irql Float State Mismatch" + }, + "fullDescription": { + "text": "The IRQL where the floating-point state was saved does not match the current IRQL (for this restore operation)." + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning" + }, + "help": { + "text": "# Irql Float State Mismatch\nThe IRQL where the floating-point state was saved does not match the current IRQL (for this restore operation).\n\n\n## Recommendation\nThe IRQL at which the driver is executing when it restores a floating-point state is different than the IRQL at which it was executing when it saved the floating-point state. Because the IRQL at which the driver runs determines how the floating-point state is saved, the driver must be executing at the same IRQL when it calls the functions to save and to restore the floating-point state.\n\n\n## Example\nExample of incorrect code. Floating point state was saved at APC_LEVEL but restored at PASSIVE_LEVEL\n\n```c\n \n\t\t_IRQL_requires_(PASSIVE_LEVEL) \n\t\tvoid driver_utility_bad(void)\n\t\t{\n\t\t\tKIRQL oldIRQL;\n\t\t\tKeRaiseIrql(APC_LEVEL, &oldIRQL);\n\t\t\t// running at APC level\n\t\t\tKFLOATING_SAVE FloatBuf;\n\t\t\tif (KeSaveFloatingPointState(&FloatBuf))\n\t\t\t{\n\t\t\t\tKeLowerIrql(oldIRQL); // lower back to PASSIVE_LEVEL\n\t\t\t\t// ...\n\t\t\t\tKeRestoreFloatingPointState(&FloatBuf);\n\t\t\t}\n\t\t}\n\t\t\n```\nCorrect example\n\n```c\n \n\t\t\t_IRQL_requires_(PASSIVE_LEVEL) \n\t\t\tvoid driver_utility_good(void)\n\t\t\t{\n\t\t\t\t// running at APC level\n\t\t\t\tKFLOATING_SAVE FloatBuf;\n\t\t\t\tKIRQL oldIRQL;\n\t\t\t\tKeRaiseIrql(APC_LEVEL, &oldIRQL);\n\n\t\t\t\tif (KeSaveFloatingPointState(&FloatBuf))\n\t\t\t\t{\n\t\t\t\t\tKeLowerIrql(oldIRQL);\n\t\t\t\t\t// ...\n\t\t\t\t\tKeRaiseIrql(APC_LEVEL, &oldIRQL);\n\t\t\t\t\tKeRestoreFloatingPointState(&FloatBuf);\n\t\t\t\t}\n\t\t\t}\n\t\t\n```\n\n## References\n* [ C28111 ](https://learn.microsoft.com/en-us/windows-hardware/drivers/devtest/28111-floating-point-irql-mismatch)\n\n## Semmle-specific notes\n**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).\n\n**Remaining limitations.** Despite the source-position and AST-loop branches, the predicate still does not detect:\n\n* **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.\n* **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.\n* **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.\n* **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.\n", + "markdown": "# Irql Float State Mismatch\nThe IRQL where the floating-point state was saved does not match the current IRQL (for this restore operation).\n\n\n## Recommendation\nThe IRQL at which the driver is executing when it restores a floating-point state is different than the IRQL at which it was executing when it saved the floating-point state. Because the IRQL at which the driver runs determines how the floating-point state is saved, the driver must be executing at the same IRQL when it calls the functions to save and to restore the floating-point state.\n\n\n## Example\nExample of incorrect code. Floating point state was saved at APC_LEVEL but restored at PASSIVE_LEVEL\n\n```c\n \n\t\t_IRQL_requires_(PASSIVE_LEVEL) \n\t\tvoid driver_utility_bad(void)\n\t\t{\n\t\t\tKIRQL oldIRQL;\n\t\t\tKeRaiseIrql(APC_LEVEL, &oldIRQL);\n\t\t\t// running at APC level\n\t\t\tKFLOATING_SAVE FloatBuf;\n\t\t\tif (KeSaveFloatingPointState(&FloatBuf))\n\t\t\t{\n\t\t\t\tKeLowerIrql(oldIRQL); // lower back to PASSIVE_LEVEL\n\t\t\t\t// ...\n\t\t\t\tKeRestoreFloatingPointState(&FloatBuf);\n\t\t\t}\n\t\t}\n\t\t\n```\nCorrect example\n\n```c\n \n\t\t\t_IRQL_requires_(PASSIVE_LEVEL) \n\t\t\tvoid driver_utility_good(void)\n\t\t\t{\n\t\t\t\t// running at APC level\n\t\t\t\tKFLOATING_SAVE FloatBuf;\n\t\t\t\tKIRQL oldIRQL;\n\t\t\t\tKeRaiseIrql(APC_LEVEL, &oldIRQL);\n\n\t\t\t\tif (KeSaveFloatingPointState(&FloatBuf))\n\t\t\t\t{\n\t\t\t\t\tKeLowerIrql(oldIRQL);\n\t\t\t\t\t// ...\n\t\t\t\t\tKeRaiseIrql(APC_LEVEL, &oldIRQL);\n\t\t\t\t\tKeRestoreFloatingPointState(&FloatBuf);\n\t\t\t\t}\n\t\t\t}\n\t\t\n```\n\n## References\n* [ C28111 ](https://learn.microsoft.com/en-us/windows-hardware/drivers/devtest/28111-floating-point-irql-mismatch)\n\n## Semmle-specific notes\n**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).\n\n**Remaining limitations.** Despite the source-position and AST-loop branches, the predicate still does not detect:\n\n* **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.\n* **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.\n* **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.\n* **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.\n" + }, + "properties": { + "tags": [ + "correctness", + "ca_ported" + ], + "description": "The IRQL where the floating-point state was saved does not match the current IRQL (for this restore operation).", + "feature.area": "Multiple", + "id": "cpp/drivers/irql-float-state-mismatch", + "impact": "Insecure Coding Practice", + "kind": "problem", + "name": "Irql Float State Mismatch", + "opaqueid": "CQLD-C28111", + "owner.email": "sdat@microsoft.com", + "platform": "Desktop", + "precision": "medium", + "problem.severity": "warning", + "query-version": "v6", + "repro.text": "The IRQL at which the driver is executing when it restores a floating-point state is different than the IRQL at which it was executing when it saved the floating-point state.\n Because the IRQL at which the driver runs determines how the floating-point state is saved, the driver must be executing at the same IRQL when it calls the functions to save and to restore the floating-point state.", + "scope": "domainspecific" + } + } + ] + }, + "extensions": [ + { + "name": "microsoft/windows-drivers", + "semanticVersion": "1.9.0+39dd725fc01992bf14a42934229a3cc1fd7bf25b", + "locations": [ + { + "uri": "file:///F:/source/repos/Windows-Driver-Developer-Supplemental-Tools/src/", + "description": { + "text": "The QL pack root directory." + }, + "properties": { + "tags": [ + "CodeQL/LocalPackRoot" ] + } }, - "extensions": [ - { - "name": "microsoft/windows-drivers", - "semanticVersion": "1.3.0+ffa7244da2c2fe57cdf6260be5d8b90e7c335336", - "locations": [ - { - "uri": "file:///C:/codeql-home/WDDST/src/", - "description": { - "text": "The QL pack root directory." - }, - "properties": { - "tags": [ - "CodeQL/LocalPackRoot" - ] - } - }, - { - "uri": "file:///C:/codeql-home/WDDST/src/qlpack.yml", - "description": { - "text": "The QL pack definition file." - }, - "properties": { - "tags": [ - "CodeQL/LocalPackDefinitionFile" - ] - } - } - ] - }, - { - "name": "codeql/cpp-all", - "semanticVersion": "3.1.0+d42788844f7ec0a6b9832140313cc2318e513987", - "locations": [ - { - "uri": "file:///C:/Users/jronstadt/.codeql/packages/codeql/cpp-all/3.1.0/", - "description": { - "text": "The QL pack root directory." - }, - "properties": { - "tags": [ - "CodeQL/LocalPackRoot" - ] - } - }, - { - "uri": "file:///C:/Users/jronstadt/.codeql/packages/codeql/cpp-all/3.1.0/qlpack.yml", - "description": { - "text": "The QL pack definition file." - }, - "properties": { - "tags": [ - "CodeQL/LocalPackDefinitionFile" - ] - } - } - ] - } - ] - }, - "invocations": [ { - "toolExecutionNotifications": [ - { - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "driver/driver_snippet.c", - "uriBaseId": "%SRCROOT%", - "index": 0 - } - } - } - ], - "message": { - "text": "" - }, - "level": "none", - "descriptor": { - "id": "cpp/baseline/expected-extracted-files", - "index": 0 - }, - "properties": { - "formattedMessage": { - "text": "" - } - } - }, - { - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "driver/fail_driver1.h", - "uriBaseId": "%SRCROOT%", - "index": 1 - } - } - } - ], - "message": { - "text": "" - }, - "level": "none", - "descriptor": { - "id": "cpp/baseline/expected-extracted-files", - "index": 0 - }, - "properties": { - "formattedMessage": { - "text": "" - } - } - }, - { - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "driver/fail_driver1.c", - "uriBaseId": "%SRCROOT%", - "index": 2 - } - } - } - ], - "message": { - "text": "" - }, - "level": "none", - "descriptor": { - "id": "cpp/baseline/expected-extracted-files", - "index": 0 - }, - "properties": { - "formattedMessage": { - "text": "" - } - } - }, - { - "message": { - "text": "Internal telemetry for the C++ extractor.\n\nNo action needed.", - "markdown": "Internal telemetry for the C++ extractor.\n\nNo action needed." - }, - "level": "note", - "timeUtc": "2025-01-17T05:02:26.230274500Z", - "descriptor": { - "id": "cpp/extractor/summary", - "index": 1 - }, - "properties": { - "attributes": { - "cache-hits": 0, - "cache-misses": 1, - "extractor-failures": 1, - "extractor-successes": 0, - "trap-caching": "disabled" - }, - "visibility": { - "statusPage": false, - "telemetry": true - } - } - } - ], - "executionSuccessful": true + "uri": "file:///F:/source/repos/Windows-Driver-Developer-Supplemental-Tools/src/qlpack.yml", + "description": { + "text": "The QL pack definition file." + }, + "properties": { + "tags": [ + "CodeQL/LocalPackDefinitionFile" + ] + } } - ], - "artifacts": [ + ] + }, + { + "name": "codeql/cpp-all", + "semanticVersion": "7.0.0+c5329f6f3863621c140ea7abd5954860e96c8bf1", + "locations": [ + { + "uri": "file:///C:/Users/natede/.codeql/packages/codeql/cpp-all/7.0.0/", + "description": { + "text": "The QL pack root directory." + }, + "properties": { + "tags": [ + "CodeQL/LocalPackRoot" + ] + } + }, { - "location": { + "uri": "file:///C:/Users/natede/.codeql/packages/codeql/cpp-all/7.0.0/qlpack.yml", + "description": { + "text": "The QL pack definition file." + }, + "properties": { + "tags": [ + "CodeQL/LocalPackDefinitionFile" + ] + } + } + ] + } + ] + }, + "invocations": [ + { + "toolExecutionNotifications": [ + { + "locations": [ + { + "physicalLocation": { + "artifactLocation": { "uri": "driver/driver_snippet.c", "uriBaseId": "%SRCROOT%", "index": 0 + } } + } + ], + "message": { + "text": "" }, - { - "location": { + "level": "none", + "descriptor": { + "id": "cpp/baseline/expected-extracted-files", + "index": 0 + }, + "properties": { + "formattedMessage": { + "text": "" + } + } + }, + { + "locations": [ + { + "physicalLocation": { + "artifactLocation": { "uri": "driver/fail_driver1.h", "uriBaseId": "%SRCROOT%", "index": 1 + } } + } + ], + "message": { + "text": "" }, - { - "location": { + "level": "none", + "descriptor": { + "id": "cpp/baseline/expected-extracted-files", + "index": 0 + }, + "properties": { + "formattedMessage": { + "text": "" + } + } + }, + { + "locations": [ + { + "physicalLocation": { + "artifactLocation": { "uri": "driver/fail_driver1.c", "uriBaseId": "%SRCROOT%", "index": 2 + } } + } + ], + "message": { + "text": "" + }, + "level": "none", + "descriptor": { + "id": "cpp/baseline/expected-extracted-files", + "index": 0 + }, + "properties": { + "formattedMessage": { + "text": "" + } } - ], - "results": [ - { - "ruleId": "cpp/drivers/irql-float-state-mismatch", - "ruleIndex": 0, - "rule": { - "id": "cpp/drivers/irql-float-state-mismatch", - "index": 0 - }, - "message": { - "text": "The irql level where the floating-point state was saved (1) does not match the irql level for the restore operation (0)." - }, - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "driver/driver_snippet.c", - "uriBaseId": "%SRCROOT%", - "index": 0 - }, - "region": { - "startLine": 23, - "startColumn": 38, - "endColumn": 46 - } - } - } - ], - "partialFingerprints": { - "primaryLocationLineHash": "b3909ff165022b51:1", - "primaryLocationStartColumnFingerprint": "29" - } + }, + { + "message": { + "text": "" }, - { - "ruleId": "cpp/drivers/irql-float-state-mismatch", - "ruleIndex": 0, - "rule": { - "id": "cpp/drivers/irql-float-state-mismatch", - "index": 0 - }, - "message": { - "text": "The irql level where the floating-point state was saved (1) does not match the irql level for the restore operation (0)." - }, - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "driver/driver_snippet.c", - "uriBaseId": "%SRCROOT%", - "index": 0 - }, - "region": { - "startLine": 23, - "startColumn": 37, - "endColumn": 46 - } - } - } + "level": "none", + "timeUtc": "2026-04-29T20:46:26.236012500Z", + "descriptor": { + "id": "cli/file-coverage-baseline", + "index": 1 + }, + "properties": { + "attributes": { + "durationMilliseconds": 233 + }, + "visibility": { + "statusPage": false, + "telemetry": true + } + } + }, + { + "message": { + "text": "" + }, + "level": "none", + "timeUtc": "2026-04-29T20:46:26.241012400Z", + "descriptor": { + "id": "cli/platform", + "index": 2 + }, + "properties": { + "attributes": { + "arch": "amd64", + "name": "Windows 11", + "version": "10.0" + }, + "visibility": { + "statusPage": false, + "telemetry": true + } + } + }, + { + "message": { + "text": "Internal telemetry for the C++ extractor.\n\nNo action needed.", + "markdown": "Internal telemetry for the C++ extractor.\n\nNo action needed." + }, + "level": "note", + "timeUtc": "2026-04-29T20:47:05.794926800Z", + "descriptor": { + "id": "cpp/extractor/summary", + "index": 3 + }, + "properties": { + "attributes": { + "cache-hits": 0, + "cache-misses": 1, + "compilers": [ + { + "program": "cl", + "version": "Microsoft (R) C/C++ Optimizing Compiler Version 19.50.35724 for x64" + } ], - "partialFingerprints": { - "primaryLocationLineHash": "b3909ff165022b51:1", - "primaryLocationStartColumnFingerprint": "28" - } + "extractor-failures": 1, + "extractor-successes": 0, + "trap-caching": "disabled" + }, + "visibility": { + "statusPage": false, + "telemetry": true + } + } + } + ], + "executionSuccessful": true + } + ], + "artifacts": [ + { + "location": { + "uri": "driver/driver_snippet.c", + "uriBaseId": "%SRCROOT%", + "index": 0 + } + }, + { + "location": { + "uri": "driver/fail_driver1.h", + "uriBaseId": "%SRCROOT%", + "index": 1 + } + }, + { + "location": { + "uri": "driver/fail_driver1.c", + "uriBaseId": "%SRCROOT%", + "index": 2 + } + } + ], + "results": [ + { + "ruleId": "cpp/drivers/irql-float-state-mismatch", + "ruleIndex": 0, + "rule": { + "id": "cpp/drivers/irql-float-state-mismatch", + "index": 0 + }, + "message": { + "text": "The irql level where the floating-point state was saved (0) does not match the irql level for the restore operation (1).\nThe irql level where the floating-point state was saved (2) does not match the irql level for the restore operation (1)." + }, + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "driver/driver_snippet.c", + "uriBaseId": "%SRCROOT%", + "index": 0 + }, + "region": { + "startLine": 156, + "startColumn": 38, + "endColumn": 46 + } + } + } + ], + "partialFingerprints": { + "primaryLocationLineHash": "7203877b7e98c247:1", + "primaryLocationStartColumnFingerprint": "29" + } + }, + { + "ruleId": "cpp/drivers/irql-float-state-mismatch", + "ruleIndex": 0, + "rule": { + "id": "cpp/drivers/irql-float-state-mismatch", + "index": 0 + }, + "message": { + "text": "The irql level where the floating-point state was saved (0) does not match the irql level for the restore operation (1).\nThe irql level where the floating-point state was saved (2) does not match the irql level for the restore operation (1)." + }, + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "driver/driver_snippet.c", + "uriBaseId": "%SRCROOT%", + "index": 0 + }, + "region": { + "startLine": 156, + "startColumn": 37, + "endColumn": 46 + } + } + } + ], + "partialFingerprints": { + "primaryLocationLineHash": "7203877b7e98c247:1", + "primaryLocationStartColumnFingerprint": "28" + } + }, + { + "ruleId": "cpp/drivers/irql-float-state-mismatch", + "ruleIndex": 0, + "rule": { + "id": "cpp/drivers/irql-float-state-mismatch", + "index": 0 + }, + "message": { + "text": "The irql level where the floating-point state was saved (0) does not match the irql level for the restore operation (2)." + }, + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "driver/driver_snippet.c", + "uriBaseId": "%SRCROOT%", + "index": 0 + }, + "region": { + "startLine": 111, + "startColumn": 33, + "endColumn": 36 + } + } + } + ], + "partialFingerprints": { + "primaryLocationLineHash": "32de180444120f36:1", + "primaryLocationStartColumnFingerprint": "28" + } + }, + { + "ruleId": "cpp/drivers/irql-float-state-mismatch", + "ruleIndex": 0, + "rule": { + "id": "cpp/drivers/irql-float-state-mismatch", + "index": 0 + }, + "message": { + "text": "The irql level where the floating-point state was saved (1) does not match the irql level for the restore operation (0)." + }, + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "driver/driver_snippet.c", + "uriBaseId": "%SRCROOT%", + "index": 0 + }, + "region": { + "startLine": 66, + "startColumn": 38, + "endColumn": 46 + } + } + } + ], + "partialFingerprints": { + "primaryLocationLineHash": "ff98177eaaf7d309:1", + "primaryLocationStartColumnFingerprint": "29" + } + }, + { + "ruleId": "cpp/drivers/irql-float-state-mismatch", + "ruleIndex": 0, + "rule": { + "id": "cpp/drivers/irql-float-state-mismatch", + "index": 0 + }, + "message": { + "text": "The irql level where the floating-point state was saved (1) does not match the irql level for the restore operation (0)." + }, + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "driver/driver_snippet.c", + "uriBaseId": "%SRCROOT%", + "index": 0 + }, + "region": { + "startLine": 66, + "startColumn": 37, + "endColumn": 46 + } + } + } + ], + "partialFingerprints": { + "primaryLocationLineHash": "ff98177eaaf7d309:1", + "primaryLocationStartColumnFingerprint": "28" + } + }, + { + "ruleId": "cpp/drivers/irql-float-state-mismatch", + "ruleIndex": 0, + "rule": { + "id": "cpp/drivers/irql-float-state-mismatch", + "index": 0 + }, + "message": { + "text": "The irql level where the floating-point state was saved (1) does not match the irql level for the restore operation (0)." + }, + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "driver/driver_snippet.c", + "uriBaseId": "%SRCROOT%", + "index": 0 + }, + "region": { + "startLine": 23, + "startColumn": 38, + "endColumn": 46 + } + } + } + ], + "partialFingerprints": { + "primaryLocationLineHash": "b3909ff165022b51:1", + "primaryLocationStartColumnFingerprint": "29" + } + }, + { + "ruleId": "cpp/drivers/irql-float-state-mismatch", + "ruleIndex": 0, + "rule": { + "id": "cpp/drivers/irql-float-state-mismatch", + "index": 0 + }, + "message": { + "text": "The irql level where the floating-point state was saved (1) does not match the irql level for the restore operation (0)." + }, + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "driver/driver_snippet.c", + "uriBaseId": "%SRCROOT%", + "index": 0 + }, + "region": { + "startLine": 23, + "startColumn": 37, + "endColumn": 46 + } } + } ], - "columnKind": "utf16CodeUnits", - "properties": { - "semmle.formatSpecifier": "sarifv2.1.0" + "partialFingerprints": { + "primaryLocationLineHash": "b3909ff165022b51:1", + "primaryLocationStartColumnFingerprint": "28" } + } + ], + "columnKind": "utf16CodeUnits", + "properties": { + "semmle.formatSpecifier": "sarifv2.1.0" } + } ] } \ No newline at end of file diff --git a/src/drivers/general/queries/IrqlFloatStateMismatch/driver_snippet.c b/src/drivers/general/queries/IrqlFloatStateMismatch/driver_snippet.c index c16d0ce1..b4ac74b6 100644 --- a/src/drivers/general/queries/IrqlFloatStateMismatch/driver_snippet.c +++ b/src/drivers/general/queries/IrqlFloatStateMismatch/driver_snippet.c @@ -40,3 +40,122 @@ void driver_utility_good(void) KeRestoreFloatingPointState(&FloatBuf); } } + +// An unannotated helper that nonetheless changes the IRQL. Real drivers +// frequently wrap KeLowerIrql / KeRaiseIrql calls in helpers without the +// _IRQL_raises_ / _IRQL_requires_same_ annotations. +static void unannotated_lower_helper(KIRQL oldIRQL) +{ + KeLowerIrql(oldIRQL); +} + +// Adversarial: the IRQL transition between save and restore happens inside +// an unannotated helper rather than directly in the enclosing function. +// A purely intraprocedural check that only inspects calls in the enclosing +// function will miss this true positive. +_IRQL_requires_(PASSIVE_LEVEL) +void driver_utility_helper_bad(void) +{ + KIRQL oldIRQL; + KeRaiseIrql(APC_LEVEL, &oldIRQL); + KFLOATING_SAVE FloatBuf; + if (KeSaveFloatingPointState(&FloatBuf)) + { + unannotated_lower_helper(oldIRQL); + // ... + KeRestoreFloatingPointState(&FloatBuf); + } +} + +// Regression guard: same as `driver_utility_good`, except the +// re-raising IRQL transition is performed inside a nested block. +// A correct check still recognises the in-function IRQL change and +// suppresses no finding here. +_IRQL_requires_(PASSIVE_LEVEL) +void driver_utility_nested_block_good(void) +{ + KFLOATING_SAVE FloatBuf; + KIRQL oldIRQL; + KeRaiseIrql(APC_LEVEL, &oldIRQL); + + if (KeSaveFloatingPointState(&FloatBuf)) + { + KeLowerIrql(oldIRQL); + { + // nested compound statement + KeRaiseIrql(APC_LEVEL, &oldIRQL); + } + KeRestoreFloatingPointState(&FloatBuf); + } +} + +// ===================================================================== +// Adversarial: cross-function save / restore via thin wrappers. +// +// `irqlChangesBetween` projects each call to an anchor line in either +// the directly enclosing function or a one-level wrapper / common +// caller, so when the save and the restore are routed through helper +// wrappers and the IRQL change happens in the caller, the caller's +// anchor lines bracket the `KeRaiseIrql` call and the mismatch is +// flagged. The IRQL at the save (PASSIVE_LEVEL) differs from the IRQL +// at the restore (DISPATCH_LEVEL) for this case. +// ===================================================================== + +static void save_fp_helper(PKFLOATING_SAVE pfs) +{ + KeSaveFloatingPointState(pfs); +} + +static void restore_fp_helper(PKFLOATING_SAVE pfs) +{ + KeRestoreFloatingPointState(pfs); +} + +_IRQL_requires_(PASSIVE_LEVEL) +void driver_utility_cross_function_bad(void) +{ + KFLOATING_SAVE FloatBuf; + KIRQL oldIRQL; + + save_fp_helper(&FloatBuf); + KeRaiseIrql(DISPATCH_LEVEL, &oldIRQL); + // ... do work at DISPATCH ... + restore_fp_helper(&FloatBuf); + KeLowerIrql(oldIRQL); +} + +// ===================================================================== +// Documentation: a loop where the restore is textually above the save. +// +// At runtime, each iteration's `KeRestoreFloatingPointState` is preceded +// by the previous iteration's `KeSaveFloatingPointState` via the loop +// back-edge, with `KeLowerIrql` in between, so the IRQL at the save +// (PASSIVE_LEVEL) does not match the IRQL at the restore (APC_LEVEL). +// Source-line position alone cannot see this because the half-open +// range [saveLine, restoreLine] is empty when the restore is textually +// earlier than the save in the function body. The AST-loop branch of +// `irqlChangesBetween` recognises this case and would fire here, but +// in our currently extracted DBs the upstream IRQL analysis library +// does not bind `getPotentialExitIrqlAtCfn` at the argument expression +// of `KeSaveFloatingPointState` when the save is inside a loop body, +// so the source-IRQL filter still rejects this case and the finding +// is suppressed. This function is retained as a documented known +// false negative; recovering it requires improvements to the IRQL +// analysis library and not just to this query. +// ===================================================================== +_IRQL_requires_(PASSIVE_LEVEL) +void driver_utility_loop_bad(void) +{ + KFLOATING_SAVE FloatBuf; + KIRQL oldIRQL; + + KeRaiseIrql(APC_LEVEL, &oldIRQL); + + for (int i = 0; i < 2; i++) + { + KeRestoreFloatingPointState(&FloatBuf); + KeLowerIrql(PASSIVE_LEVEL); + KeSaveFloatingPointState(&FloatBuf); + KeRaiseIrql(APC_LEVEL, &oldIRQL); + } +} diff --git a/src/drivers/general/queries/IrqlInconsistentWithRequired/IrqlInconsistentWithRequired.ql b/src/drivers/general/queries/IrqlInconsistentWithRequired/IrqlInconsistentWithRequired.ql index 344f01e0..6c32239c 100644 --- a/src/drivers/general/queries/IrqlInconsistentWithRequired/IrqlInconsistentWithRequired.ql +++ b/src/drivers/general/queries/IrqlInconsistentWithRequired/IrqlInconsistentWithRequired.ql @@ -30,7 +30,14 @@ where entryCfn = f.getBlock() and irqlLevelEntry = getPotentialExitIrqlAtCfn(entryCfn) and irqlLevelExit = getPotentialExitIrqlAtCfn(exitCfn) and - irqlLevelEntry != irqlLevelExit + irqlLevelEntry != irqlLevelExit and + // Soundness filter: only flag if `f` actually contains an IRQL-changing + // call (directly or transitively through a wrapper). This is necessary + // due to our IRQL CFG analysis producing ranges of potential IRQLs. + exists(FunctionCall fc | + fc.getEnclosingFunction() = f and + isIrqlChangingCall(fc) + ) select f, "Possible IRQL level at function completion inconsistent with the required IRQL level for some path. Irql level expected: " + irqlLevelEntry + ". Irql level found: " + irqlLevelExit + diff --git a/src/drivers/general/queries/IrqlNotSaved/IrqlNotSaved.ql b/src/drivers/general/queries/IrqlNotSaved/IrqlNotSaved.ql index 43e2a076..160313dd 100644 --- a/src/drivers/general/queries/IrqlNotSaved/IrqlNotSaved.ql +++ b/src/drivers/general/queries/IrqlNotSaved/IrqlNotSaved.ql @@ -18,7 +18,7 @@ * ca_ported * wddst * @scope domainspecific - * @query-version v1 + * @query-version v2 */ import cpp @@ -74,15 +74,20 @@ class FundamentalIrqlSaveFunction extends IrqlSavesFunction { } /** - * A simple data flow from any IrqlSaveParameter. + * A data flow from any IrqlSaveParameter to variables that receive its value. */ module IrqlSaveParameterFlowConfigurationConfig implements DataFlow::ConfigSig { - predicate isSource(DataFlow::Node source) { + predicate isSource(DataFlow::Node source) { source.asParameter() instanceof IrqlSaveParameter } - predicate isSink(DataFlow::Node sink) { sink instanceof DataFlow::Node } + predicate isSink(DataFlow::Node sink) { + // Only track flow to assignment targets or parameters, not every node. + exists(Variable v | v.getAnAssignedValue() = sink.asExpr()) + or + sink.asParameter() instanceof Parameter + } } module IrqlSaveParameterFlowConfiguration = DataFlow::Global; diff --git a/src/drivers/general/queries/IrqlNotSaved/IrqlNotSaved.sarif b/src/drivers/general/queries/IrqlNotSaved/IrqlNotSaved.sarif index ebd33304..1dd6ed36 100644 --- a/src/drivers/general/queries/IrqlNotSaved/IrqlNotSaved.sarif +++ b/src/drivers/general/queries/IrqlNotSaved/IrqlNotSaved.sarif @@ -1,219 +1,413 @@ { - "$schema" : "https://json.schemastore.org/sarif-2.1.0.json", - "version" : "2.1.0", - "runs" : [ { - "tool" : { - "driver" : { - "name" : "CodeQL", - "organization" : "GitHub", - "semanticVersion" : "2.14.4", - "notifications" : [ { - "id" : "cpp/baseline/expected-extracted-files", - "name" : "cpp/baseline/expected-extracted-files", - "shortDescription" : { - "text" : "Expected extracted files" - }, - "fullDescription" : { - "text" : "Files appearing in the source archive that are expected to be extracted." - }, - "defaultConfiguration" : { - "enabled" : true - }, - "properties" : { - "tags" : [ "expected-extracted-files", "telemetry" ] - } - } ], - "rules" : [ { - "id" : "cpp/drivers/irql-not-saved", - "name" : "cpp/drivers/irql-not-saved", - "shortDescription" : { - "text" : "IRQL not saved (C28158)" - }, - "fullDescription" : { - "text" : "A variable annotated \\_IRQL\\_saves\\_ must have the IRQL saved into it." - }, - "defaultConfiguration" : { - "enabled" : true, - "level" : "warning" + "$schema": "https://json.schemastore.org/sarif-2.1.0.json", + "version": "2.1.0", + "runs": [ + { + "tool": { + "driver": { + "name": "CodeQL", + "organization": "GitHub", + "semanticVersion": "2.24.2", + "notifications": [ + { + "id": "cpp/baseline/expected-extracted-files", + "name": "cpp/baseline/expected-extracted-files", + "shortDescription": { + "text": "Expected extracted files" + }, + "fullDescription": { + "text": "Files appearing in the source archive that are expected to be extracted." + }, + "defaultConfiguration": { + "enabled": true + }, + "properties": { + "tags": [ + "expected-extracted-files", + "telemetry" + ] + } + }, + { + "id": "cli/file-coverage-baseline", + "name": "cli/file-coverage-baseline", + "shortDescription": { + "text": "File coverage baseline telemetry" + }, + "fullDescription": { + "text": "File coverage baseline telemetry" + }, + "defaultConfiguration": { + "enabled": true + } + }, + { + "id": "cli/platform", + "name": "cli/platform", + "shortDescription": { + "text": "Platform" + }, + "fullDescription": { + "text": "Platform" + }, + "defaultConfiguration": { + "enabled": true + } + }, + { + "id": "cpp/extractor/summary", + "name": "cpp/extractor/summary", + "shortDescription": { + "text": "C++ extractor telemetry" + }, + "fullDescription": { + "text": "C++ extractor telemetry" + }, + "defaultConfiguration": { + "enabled": true + } + } + ], + "rules": [ + { + "id": "cpp/drivers/irql-not-saved", + "name": "cpp/drivers/irql-not-saved", + "shortDescription": { + "text": "IRQL not saved (C28158)" + }, + "fullDescription": { + "text": "A variable annotated \\_IRQL\\_saves\\_ must have the IRQL saved into it." + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning" + }, + "help": { + "text": "# IRQL not saved (C28158)\r\nA parameter annotated _IRQL_saves_ must have the IRQL value saved to it.\r\n\r\n\r\n## Recommendation\r\nMake sure that any parameter annotated \"_IRQL_saves_\" has a code path where the current system IRQL is saved to it.\r\n\r\n\r\n## Example\r\nIn this example, the driver does not save the IRQL to a parameter annotated to have the IRQL saved to it:\r\n\r\n```c\r\n \r\n\t\tVOID IrqlNotSaved_fail(_IRQL_saves_ KIRQL outIrql, PKSPIN_LOCK myLock) {\r\n\t\t\tKIRQL localIrql;\r\n\t\t\tKeAcquireSpinLock(myLock, &localIrql);\r\n\t\t}\r\n\t\t\r\n```\r\nThe driver should make sure to save the IRQL in the annotated parameter.\r\n\r\n```c\r\n\r\n\t\tVOID IrqlNotSaved_pass(_IRQL_saves_ KIRQL outIrql, PKSPIN_LOCK myLock) {\r\n\t\t\tKeAcquireSpinLock(myLock, &outIrqlPass);\r\n\t\t}\r\n\t\t\r\n```\r\n\r\n## Semmle-specific notes\r\nThis version of the query only checks for functions that have no path where the IRQL is saved at all. In a future update, we will use the must-flow library to check for functions where there are _any_ paths where the IRQL is not saved.\r\n\r\nFalse positives may occur if UNREFERENCED_PARAMETER() is used on an annotated parameter.\r\n\r\n\r\n## References\r\n* [ C28158 warning - Windows Drivers ](https://learn.microsoft.com/en-us/windows-hardware/drivers/devtest/28158-no-irql-was-saved)\r\n", + "markdown": "# IRQL not saved (C28158)\r\nA parameter annotated _IRQL_saves_ must have the IRQL value saved to it.\r\n\r\n\r\n## Recommendation\r\nMake sure that any parameter annotated \"_IRQL_saves_\" has a code path where the current system IRQL is saved to it.\r\n\r\n\r\n## Example\r\nIn this example, the driver does not save the IRQL to a parameter annotated to have the IRQL saved to it:\r\n\r\n```c\r\n \r\n\t\tVOID IrqlNotSaved_fail(_IRQL_saves_ KIRQL outIrql, PKSPIN_LOCK myLock) {\r\n\t\t\tKIRQL localIrql;\r\n\t\t\tKeAcquireSpinLock(myLock, &localIrql);\r\n\t\t}\r\n\t\t\r\n```\r\nThe driver should make sure to save the IRQL in the annotated parameter.\r\n\r\n```c\r\n\r\n\t\tVOID IrqlNotSaved_pass(_IRQL_saves_ KIRQL outIrql, PKSPIN_LOCK myLock) {\r\n\t\t\tKeAcquireSpinLock(myLock, &outIrqlPass);\r\n\t\t}\r\n\t\t\r\n```\r\n\r\n## Semmle-specific notes\r\nThis version of the query only checks for functions that have no path where the IRQL is saved at all. In a future update, we will use the must-flow library to check for functions where there are _any_ paths where the IRQL is not saved.\r\n\r\nFalse positives may occur if UNREFERENCED_PARAMETER() is used on an annotated parameter.\r\n\r\n\r\n## References\r\n* [ C28158 warning - Windows Drivers ](https://learn.microsoft.com/en-us/windows-hardware/drivers/devtest/28158-no-irql-was-saved)\r\n" + }, + "properties": { + "tags": [ + "correctness", + "ca_ported", + "wddst" + ], + "description": "A variable annotated \\_IRQL\\_saves\\_ must have the IRQL saved into it.", + "feature.area": "Multiple", + "id": "cpp/drivers/irql-not-saved", + "impact": "Insecure Coding Practice", + "kind": "problem", + "name": "IRQL not saved (C28158)", + "opaqueid": "CQLD-C28158", + "owner.email": "sdat@microsoft.com", + "platform": "Desktop", + "precision": "medium", + "problem.severity": "warning", + "query-version": "v2", + "repro.text": "This function has a parameter annotated \\_IRQL\\_saves\\_, but does not have the system IRQL saved to it.", + "scope": "domainspecific", + "security.severity": "Low" + } + } + ] + }, + "extensions": [ + { + "name": "microsoft/windows-drivers", + "semanticVersion": "1.9.0+a76f8551b47f01adada99ddc44a5ea4fa9839fca", + "locations": [ + { + "uri": "file:///F:/source/repos/Windows-Driver-Developer-Supplemental-Tools/src/", + "description": { + "text": "The QL pack root directory." + }, + "properties": { + "tags": [ + "CodeQL/LocalPackRoot" + ] + } + }, + { + "uri": "file:///F:/source/repos/Windows-Driver-Developer-Supplemental-Tools/src/qlpack.yml", + "description": { + "text": "The QL pack definition file." + }, + "properties": { + "tags": [ + "CodeQL/LocalPackDefinitionFile" + ] + } + } + ] }, - "properties" : { - "tags" : [ "correctness", "wddst" ], - "description" : "A variable annotated \\_IRQL\\_saves\\_ must have the IRQL saved into it.", - "feature.area" : "Multiple", - "id" : "cpp/drivers/irql-not-saved", - "impact" : "Insecure Coding Practice", - "kind" : "problem", - "name" : "IRQL not saved (C28158)", - "opaqueid" : "CQLD-C28158", - "owner.email" : "sdat@microsoft.com", - "platform" : "Desktop", - "precision" : "medium", - "problem.severity" : "warning", - "query-version" : "v1", - "repro.text" : "This function has a parameter annotated \\_IRQL\\_saves\\_, but does not have the system IRQL saved to it.", - "scope" : "domainspecific", - "security.severity" : "Low" + { + "name": "codeql/cpp-all", + "semanticVersion": "7.0.0+c5329f6f3863621c140ea7abd5954860e96c8bf1", + "locations": [ + { + "uri": "file:///C:/Users/natede/.codeql/packages/codeql/cpp-all/7.0.0/", + "description": { + "text": "The QL pack root directory." + }, + "properties": { + "tags": [ + "CodeQL/LocalPackRoot" + ] + } + }, + { + "uri": "file:///C:/Users/natede/.codeql/packages/codeql/cpp-all/7.0.0/qlpack.yml", + "description": { + "text": "The QL pack definition file." + }, + "properties": { + "tags": [ + "CodeQL/LocalPackDefinitionFile" + ] + } + } + ] } - } ] + ] }, - "extensions" : [ { - "name" : "microsoft/windows-drivers", - "semanticVersion" : "0.2.0+be14ce4fcaa9006e7636d8605fc53ea7c422a561", - "locations" : [ { - "uri" : "file:///C:/codeql-home/Windows-Driver-Developer-Supplemental-Tools/src/", - "description" : { - "text" : "The QL pack root directory." - } - }, { - "uri" : "file:///C:/codeql-home/Windows-Driver-Developer-Supplemental-Tools/src/qlpack.yml", - "description" : { - "text" : "The QL pack definition file." - } - } ] - } ] - }, - "invocations" : [ { - "toolExecutionNotifications" : [ { - "locations" : [ { - "physicalLocation" : { - "artifactLocation" : { - "uri" : "driver/fail_driver1.c", - "uriBaseId" : "%SRCROOT%", - "index" : 1 + "invocations": [ + { + "toolExecutionNotifications": [ + { + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "driver/driver_snippet.c", + "uriBaseId": "%SRCROOT%", + "index": 0 + } + } + } + ], + "message": { + "text": "" + }, + "level": "none", + "descriptor": { + "id": "cpp/baseline/expected-extracted-files", + "index": 0 + }, + "properties": { + "formattedMessage": { + "text": "" + } + } + }, + { + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "driver/fail_driver1.h", + "uriBaseId": "%SRCROOT%", + "index": 1 + } + } + } + ], + "message": { + "text": "" + }, + "level": "none", + "descriptor": { + "id": "cpp/baseline/expected-extracted-files", + "index": 0 + }, + "properties": { + "formattedMessage": { + "text": "" + } + } + }, + { + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "driver/fail_driver1.c", + "uriBaseId": "%SRCROOT%", + "index": 2 + } + } + } + ], + "message": { + "text": "" + }, + "level": "none", + "descriptor": { + "id": "cpp/baseline/expected-extracted-files", + "index": 0 + }, + "properties": { + "formattedMessage": { + "text": "" + } + } + }, + { + "message": { + "text": "" + }, + "level": "none", + "timeUtc": "2026-04-29T04:44:06.842402600Z", + "descriptor": { + "id": "cli/file-coverage-baseline", + "index": 1 + }, + "properties": { + "attributes": { + "durationMilliseconds": 268 + }, + "visibility": { + "statusPage": false, + "telemetry": true + } + } + }, + { + "message": { + "text": "" + }, + "level": "none", + "timeUtc": "2026-04-29T04:44:06.848399600Z", + "descriptor": { + "id": "cli/platform", + "index": 2 + }, + "properties": { + "attributes": { + "arch": "amd64", + "name": "Windows 11", + "version": "10.0" + }, + "visibility": { + "statusPage": false, + "telemetry": true + } + } + }, + { + "message": { + "text": "Internal telemetry for the C++ extractor.\n\nNo action needed.", + "markdown": "Internal telemetry for the C++ extractor.\n\nNo action needed." + }, + "level": "note", + "timeUtc": "2026-04-29T04:44:50.137416100Z", + "descriptor": { + "id": "cpp/extractor/summary", + "index": 3 + }, + "properties": { + "attributes": { + "cache-hits": 0, + "cache-misses": 1, + "compilers": [ + { + "program": "cl", + "version": "Microsoft (R) C/C++ Optimizing Compiler Version 19.50.35724 for x64" + } + ], + "extractor-failures": 1, + "extractor-successes": 0, + "trap-caching": "disabled" + }, + "visibility": { + "statusPage": false, + "telemetry": true + } + } } - } - } ], - "message" : { - "text" : "" - }, - "level" : "none", - "descriptor" : { - "id" : "cpp/baseline/expected-extracted-files", - "index" : 0 - }, - "properties" : { - "formattedMessage" : { - "text" : "" - } + ], + "executionSuccessful": true } - }, { - "locations" : [ { - "physicalLocation" : { - "artifactLocation" : { - "uri" : "driver/driver_snippet.c", - "uriBaseId" : "%SRCROOT%", - "index" : 0 - } + ], + "artifacts": [ + { + "location": { + "uri": "driver/driver_snippet.c", + "uriBaseId": "%SRCROOT%", + "index": 0 } - } ], - "message" : { - "text" : "" }, - "level" : "none", - "descriptor" : { - "id" : "cpp/baseline/expected-extracted-files", - "index" : 0 - }, - "properties" : { - "formattedMessage" : { - "text" : "" + { + "location": { + "uri": "driver/fail_driver1.h", + "uriBaseId": "%SRCROOT%", + "index": 1 } - } - }, { - "locations" : [ { - "physicalLocation" : { - "artifactLocation" : { - "uri" : "driver/fail_driver1.h", - "uriBaseId" : "%SRCROOT%", - "index" : 2 - } - } - } ], - "message" : { - "text" : "" - }, - "level" : "none", - "descriptor" : { - "id" : "cpp/baseline/expected-extracted-files", - "index" : 0 }, - "properties" : { - "formattedMessage" : { - "text" : "" + { + "location": { + "uri": "driver/fail_driver1.c", + "uriBaseId": "%SRCROOT%", + "index": 2 } } - } ], - "executionSuccessful" : true - } ], - "artifacts" : [ { - "location" : { - "uri" : "driver/driver_snippet.c", - "uriBaseId" : "%SRCROOT%", - "index" : 0 - } - }, { - "location" : { - "uri" : "driver/fail_driver1.c", - "uriBaseId" : "%SRCROOT%", - "index" : 1 - } - }, { - "location" : { - "uri" : "driver/fail_driver1.h", - "uriBaseId" : "%SRCROOT%", - "index" : 2 - } - } ], - "results" : [ { - "ruleId" : "cpp/drivers/irql-not-saved", - "ruleIndex" : 0, - "rule" : { - "id" : "cpp/drivers/irql-not-saved", - "index" : 0 - }, - "message" : { - "text" : "The parameter [outIrqlFail](1) is annotated \"_IRQL_saves_\" but never has the IRQL saved to it." - }, - "locations" : [ { - "physicalLocation" : { - "artifactLocation" : { - "uri" : "driver/driver_snippet.c", - "uriBaseId" : "%SRCROOT%", - "index" : 0 + ], + "results": [ + { + "ruleId": "cpp/drivers/irql-not-saved", + "ruleIndex": 0, + "rule": { + "id": "cpp/drivers/irql-not-saved", + "index": 0 }, - "region" : { - "startLine" : 51, - "startColumn" : 44, - "endColumn" : 55 - } - } - } ], - "partialFingerprints" : { - "primaryLocationLineHash" : "e054bbd9d7acc717:1", - "primaryLocationStartColumnFingerprint" : "43" - }, - "relatedLocations" : [ { - "id" : 1, - "physicalLocation" : { - "artifactLocation" : { - "uri" : "driver/driver_snippet.c", - "uriBaseId" : "%SRCROOT%", - "index" : 0 + "message": { + "text": "The parameter [outIrqlFail](1) is annotated \"_IRQL_saves_\" but never has the IRQL saved to it." }, - "region" : { - "startLine" : 51, - "startColumn" : 44, - "endColumn" : 55 - } - }, - "message" : { - "text" : "outIrqlFail" + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "driver/driver_snippet.c", + "uriBaseId": "%SRCROOT%", + "index": 0 + }, + "region": { + "startLine": 51, + "startColumn": 44, + "endColumn": 55 + } + } + } + ], + "partialFingerprints": { + "primaryLocationLineHash": "e054bbd9d7acc717:1", + "primaryLocationStartColumnFingerprint": "43" + }, + "relatedLocations": [ + { + "id": 1, + "physicalLocation": { + "artifactLocation": { + "uri": "driver/driver_snippet.c", + "uriBaseId": "%SRCROOT%", + "index": 0 + }, + "region": { + "startLine": 51, + "startColumn": 44, + "endColumn": 55 + } + }, + "message": { + "text": "outIrqlFail" + } + } + ] } - } ] - } ], - "columnKind" : "utf16CodeUnits", - "properties" : { - "semmle.formatSpecifier" : "sarifv2.1.0" + ], + "columnKind": "utf16CodeUnits", + "properties": { + "semmle.formatSpecifier": "sarifv2.1.0" + } } - } ] -} \ No newline at end of file + ] +} diff --git a/src/drivers/general/queries/IrqlSetTooHigh/IrqlSetTooHigh.md b/src/drivers/general/queries/IrqlSetTooHigh/IrqlSetTooHigh.md index b83ce92f..13ddd8fa 100644 --- a/src/drivers/general/queries/IrqlSetTooHigh/IrqlSetTooHigh.md +++ b/src/drivers/general/queries/IrqlSetTooHigh/IrqlSetTooHigh.md @@ -28,3 +28,5 @@ This query may provide false positives in cases where functions are not annotate 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. +**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)`. + diff --git a/src/drivers/general/queries/IrqlSetTooHigh/IrqlSetTooHigh.qhelp b/src/drivers/general/queries/IrqlSetTooHigh/IrqlSetTooHigh.qhelp index baa360a9..f3b44cb9 100644 --- a/src/drivers/general/queries/IrqlSetTooHigh/IrqlSetTooHigh.qhelp +++ b/src/drivers/general/queries/IrqlSetTooHigh/IrqlSetTooHigh.qhelp @@ -36,5 +36,21 @@

This query uses interprocedural data-flow analysis and can take a large amount of CPU time and memory to run.

This query may provide false positives in cases where functions are not annotated with their expected IRQL ranges or behaviors.

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.

+ +

+ 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). +

diff --git a/src/drivers/general/queries/IrqlSetTooHigh/IrqlSetTooHigh.ql b/src/drivers/general/queries/IrqlSetTooHigh/IrqlSetTooHigh.ql index 56d32680..0d5134b0 100644 --- a/src/drivers/general/queries/IrqlSetTooHigh/IrqlSetTooHigh.ql +++ b/src/drivers/general/queries/IrqlSetTooHigh/IrqlSetTooHigh.ql @@ -18,7 +18,7 @@ * ca_ported * wddst * @scope domainspecific - * @query-version v1 + * @query-version v2 */ import cpp @@ -39,9 +39,16 @@ where irqlFunc.(IrqlAlwaysMaxFunction).getIrqlLevel() = irqlRequirement or // If we don't have an explicit max annotation but do raise the IRQL, - // we treat the raised-to level as the implicit max. + // we treat the raised-to level as the implicit max -- + // UNLESS the function has a _requires_min_ above the raises-to level, + // which indicates a "lower IRQL on exit" pattern (e.g. mutex release), + // not a ceiling. not irqlFunc instanceof IrqlAlwaysMaxFunction and - irqlFunc.(IrqlRaisesAnnotatedFunction).getIrqlLevel() = irqlRequirement + irqlFunc.(IrqlRaisesAnnotatedFunction).getIrqlLevel() = irqlRequirement and + not exists(IrqlMinAnnotation minAnno | + minAnno = irqlFunc.getFuncIrqlAnnotation() and + minAnno.getIrqlLevel() > irqlRequirement + ) ) and tooHighForFunc(irqlFunc, statement, irqlRequirement) and // Only get the first node which is set too low diff --git a/src/drivers/general/queries/IrqlSetTooHigh/IrqlSetTooHigh.sarif b/src/drivers/general/queries/IrqlSetTooHigh/IrqlSetTooHigh.sarif index c806a95a..83b7330a 100644 --- a/src/drivers/general/queries/IrqlSetTooHigh/IrqlSetTooHigh.sarif +++ b/src/drivers/general/queries/IrqlSetTooHigh/IrqlSetTooHigh.sarif @@ -7,7 +7,7 @@ "driver": { "name": "CodeQL", "organization": "GitHub", - "semanticVersion": "2.23.3", + "semanticVersion": "2.24.2", "notifications": [ { "id": "cpp/baseline/expected-extracted-files", @@ -28,6 +28,19 @@ ] } }, + { + "id": "cli/file-coverage-baseline", + "name": "cli/file-coverage-baseline", + "shortDescription": { + "text": "File coverage baseline telemetry" + }, + "fullDescription": { + "text": "File coverage baseline telemetry" + }, + "defaultConfiguration": { + "enabled": true + } + }, { "id": "cli/platform", "name": "cli/platform", @@ -69,6 +82,10 @@ "enabled": true, "level": "warning" }, + "help": { + "text": "# IRQL set too high (C28150)\nThe function has raised the IRQL to a level above what is allowed.\n\n\n## Recommendation\nA function has been annotated as having a max IRQL, but the execution of that function raises the IRQL above that maximum. If you have applied custom IRQL annotations to your own functions, confirm that they are accurate.\n\n\n## Example\nIn this example, the driver tries to raise the IRQL to HIGH_LEVEL while in a dispatch routine. This should be avoided.\n\n```c\n\n\t\t\t// Within a dispatch routine\n\t\t\tKeRaiseIrql(HIGH_LEVEL, &oldIRQL);\n\t\t\t\n\t\t\n```\n\n## References\n* [ C28150 warning - Windows Drivers ](https://learn.microsoft.com/en-us/windows-hardware/drivers/devtest/28150-function-causes-irq-level-to-be-set-above-max)\n* [ IRQL annotations for drivers ](https://learn.microsoft.com/en-us/windows-hardware/drivers/devtest/irql-annotations-for-drivers)\n\n## Semmle-specific notes\nThis query uses interprocedural data-flow analysis and can take a large amount of CPU time and memory to run.\n\nThis query may provide false positives in cases where functions are not annotated with their expected IRQL ranges or behaviors.\n\nFor information on how to annotate your functions with information about how they adjust the IRQL, see \"IRQL annotations for drivers\" in the references section.\n\n**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.\n\nThis 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.\n\n", + "markdown": "# IRQL set too high (C28150)\nThe function has raised the IRQL to a level above what is allowed.\n\n\n## Recommendation\nA function has been annotated as having a max IRQL, but the execution of that function raises the IRQL above that maximum. If you have applied custom IRQL annotations to your own functions, confirm that they are accurate.\n\n\n## Example\nIn this example, the driver tries to raise the IRQL to HIGH_LEVEL while in a dispatch routine. This should be avoided.\n\n```c\n\n\t\t\t// Within a dispatch routine\n\t\t\tKeRaiseIrql(HIGH_LEVEL, &oldIRQL);\n\t\t\t\n\t\t\n```\n\n## References\n* [ C28150 warning - Windows Drivers ](https://learn.microsoft.com/en-us/windows-hardware/drivers/devtest/28150-function-causes-irq-level-to-be-set-above-max)\n* [ IRQL annotations for drivers ](https://learn.microsoft.com/en-us/windows-hardware/drivers/devtest/irql-annotations-for-drivers)\n\n## Semmle-specific notes\nThis query uses interprocedural data-flow analysis and can take a large amount of CPU time and memory to run.\n\nThis query may provide false positives in cases where functions are not annotated with their expected IRQL ranges or behaviors.\n\nFor information on how to annotate your functions with information about how they adjust the IRQL, see \"IRQL annotations for drivers\" in the references section.\n\n**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.\n\nThis 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.\n\n" + }, "properties": { "tags": [ "correctness", @@ -86,7 +103,7 @@ "platform": "Desktop", "precision": "medium", "problem.severity": "warning", - "query-version": "v1", + "query-version": "v2", "repro.text": "The following statement exits at an IRQL too high for the function it is contained in.", "scope": "domainspecific", "security.severity": "Low" @@ -97,10 +114,10 @@ "extensions": [ { "name": "microsoft/windows-drivers", - "semanticVersion": "1.8.1+801b2d9a470acb3a6f2beddebaff099855c9ac8e", + "semanticVersion": "1.9.0+a76f8551b47f01adada99ddc44a5ea4fa9839fca", "locations": [ { - "uri": "file:///D:/source/repos/Windows-Driver-Developer-Supplemental-Tools/src/", + "uri": "file:///F:/source/repos/Windows-Driver-Developer-Supplemental-Tools/src/", "description": { "text": "The QL pack root directory." }, @@ -111,7 +128,7 @@ } }, { - "uri": "file:///D:/source/repos/Windows-Driver-Developer-Supplemental-Tools/src/qlpack.yml", + "uri": "file:///F:/source/repos/Windows-Driver-Developer-Supplemental-Tools/src/qlpack.yml", "description": { "text": "The QL pack definition file." }, @@ -125,10 +142,10 @@ }, { "name": "codeql/cpp-all", - "semanticVersion": "4.2.0+2409bcc0d62644acbc432900bc59c2e3ff33bd56", + "semanticVersion": "7.0.0+c5329f6f3863621c140ea7abd5954860e96c8bf1", "locations": [ { - "uri": "file:///C:/Users/natede/.codeql/packages/codeql/cpp-all/4.2.0/", + "uri": "file:///C:/Users/natede/.codeql/packages/codeql/cpp-all/7.0.0/", "description": { "text": "The QL pack root directory." }, @@ -139,7 +156,7 @@ } }, { - "uri": "file:///C:/Users/natede/.codeql/packages/codeql/cpp-all/4.2.0/qlpack.yml", + "uri": "file:///C:/Users/natede/.codeql/packages/codeql/cpp-all/7.0.0/qlpack.yml", "description": { "text": "The QL pack definition file." }, @@ -236,15 +253,34 @@ }, { "message": { - "text": "On the Windows 11 (amd64; 10.0) platform.", - "markdown": "On the Windows 11 (amd64; 10.0) platform." + "text": "" }, "level": "none", - "timeUtc": "2026-01-23T07:54:15.471875500Z", + "timeUtc": "2026-04-29T04:47:08.489157300Z", "descriptor": { - "id": "cli/platform", + "id": "cli/file-coverage-baseline", "index": 1 }, + "properties": { + "attributes": { + "durationMilliseconds": 312 + }, + "visibility": { + "statusPage": false, + "telemetry": true + } + } + }, + { + "message": { + "text": "" + }, + "level": "none", + "timeUtc": "2026-04-29T04:47:08.494163300Z", + "descriptor": { + "id": "cli/platform", + "index": 2 + }, "properties": { "attributes": { "arch": "amd64", @@ -263,10 +299,10 @@ "markdown": "Internal telemetry for the C++ extractor.\n\nNo action needed." }, "level": "note", - "timeUtc": "2026-01-23T15:54:25.920679Z", + "timeUtc": "2026-04-29T04:47:58.301890800Z", "descriptor": { "id": "cpp/extractor/summary", - "index": 2 + "index": 3 }, "properties": { "attributes": { @@ -275,7 +311,7 @@ "compilers": [ { "program": "cl", - "version": "Microsoft (R) C/C++ Optimizing Compiler Version 19.44.35222 for x64" + "version": "Microsoft (R) C/C++ Optimizing Compiler Version 19.50.35724 for x64" } ], "extractor-failures": 1, @@ -599,4 +635,4 @@ } } ] -} \ No newline at end of file +} diff --git a/src/drivers/general/queries/IrqlTooHigh/IrqlTooHigh.md b/src/drivers/general/queries/IrqlTooHigh/IrqlTooHigh.md index 2bb42bd8..5dc5e8be 100644 --- a/src/drivers/general/queries/IrqlTooHigh/IrqlTooHigh.md +++ b/src/drivers/general/queries/IrqlTooHigh/IrqlTooHigh.md @@ -53,3 +53,5 @@ This query may provide false positives in cases where functions are not annotate 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. +**Dead-branch suppression.** The query suppresses calls inside `if (b)` when `b` is either a compile-time constant `0`, or a non-`static` local initialized to `FALSE`/`0` that is never reassigned, incremented/decremented, or address-taken in the enclosing function. Targets dead-branch patterns from NDIS macros such as `FILTER_ACQUIRE_LOCK(lock, bFalse)`. The variable conditions are deliberately strict to avoid suppressing real findings on globals, function-statics, or values mutated through a pointer parameter. + diff --git a/src/drivers/general/queries/IrqlTooHigh/IrqlTooHigh.qhelp b/src/drivers/general/queries/IrqlTooHigh/IrqlTooHigh.qhelp index 9b560db9..0dd309d5 100644 --- a/src/drivers/general/queries/IrqlTooHigh/IrqlTooHigh.qhelp +++ b/src/drivers/general/queries/IrqlTooHigh/IrqlTooHigh.qhelp @@ -60,5 +60,19 @@

This query uses interprocedural data-flow analysis and can take a large amount of CPU time and memory to run.

This query may provide false positives in cases where functions are not annotated with their expected IRQL ranges or behaviors.

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.

+ +

+ Dead-branch suppression. The query suppresses calls + inside if (b) when b is either a + compile-time constant 0, or a + non-static local initialized to + FALSE/0 that is never reassigned, + incremented/decremented, or address-taken in the enclosing + function. Targets dead-branch patterns from NDIS macros such + as FILTER_ACQUIRE_LOCK(lock, bFalse). The + variable conditions are deliberately strict to avoid + suppressing real findings on globals, function-statics, or + values mutated through a pointer parameter. +

diff --git a/src/drivers/general/queries/IrqlTooHigh/IrqlTooHigh.ql b/src/drivers/general/queries/IrqlTooHigh/IrqlTooHigh.ql index b49a762e..b3a8313a 100644 --- a/src/drivers/general/queries/IrqlTooHigh/IrqlTooHigh.ql +++ b/src/drivers/general/queries/IrqlTooHigh/IrqlTooHigh.ql @@ -18,7 +18,7 @@ * ca_ported * wddst * @scope domainspecific - * @query-version v3 + * @query-version v5 */ import cpp @@ -35,7 +35,8 @@ where irqlRequirement = ifa.getIrqlLevel() and irqlRequirement != -1 and irqlRequirement < min(getPotentialExitIrqlAtCfn(prior)) and - not ifa.whenConditionIsFalseAtCallSite(call) + not ifa.whenConditionIsFalseAtCallSite(call) and + not isInConstantFalseBranch(call) select call, "$@: IRQL potentially too high at call to $@. Maximum IRQL for this call: " + irqlRequirement + ", IRQL at preceding node: " + min(getPotentialExitIrqlAtCfn(prior)), diff --git a/src/drivers/general/queries/IrqlTooHigh/IrqlTooHigh.sarif b/src/drivers/general/queries/IrqlTooHigh/IrqlTooHigh.sarif index 6cdc3298..6f5b52c9 100644 --- a/src/drivers/general/queries/IrqlTooHigh/IrqlTooHigh.sarif +++ b/src/drivers/general/queries/IrqlTooHigh/IrqlTooHigh.sarif @@ -2,528 +2,911 @@ "$schema": "https://json.schemastore.org/sarif-2.1.0.json", "version": "2.1.0", "runs": [ - { - "tool": { - "driver": { - "name": "CodeQL", - "organization": "GitHub", - "semanticVersion": "2.18.4", - "notifications": [ - { - "id": "cpp/baseline/expected-extracted-files", - "name": "cpp/baseline/expected-extracted-files", - "shortDescription": { - "text": "Expected extracted files" - }, - "fullDescription": { - "text": "Files appearing in the source archive that are expected to be extracted." - }, - "defaultConfiguration": { - "enabled": true - }, - "properties": { - "tags": [ - "expected-extracted-files", - "telemetry" - ] - } - }, - { - "id": "cpp/extractor/summary", - "name": "cpp/extractor/summary", - "shortDescription": { - "text": "C++ extractor telemetry" - }, - "fullDescription": { - "text": "C++ extractor telemetry" - }, - "defaultConfiguration": { - "enabled": true - } - } - ], - "rules": [ - { - "id": "cpp/drivers/irql-too-high", - "name": "cpp/drivers/irql-too-high", - "shortDescription": { - "text": "IRQL too high (C28121)" - }, - "fullDescription": { - "text": "A function annotated with IRQL requirements was called at an IRQL too high for the requirements." - }, - "defaultConfiguration": { - "enabled": true, - "level": "warning" - }, - "properties": { - "tags": [ - "correctness", - "wddst" - ], - "description": "A function annotated with IRQL requirements was called at an IRQL too high for the requirements.", - "feature.area": "Multiple", - "id": "cpp/drivers/irql-too-high", - "impact": "Exploitable Design", - "kind": "problem", - "name": "IRQL too high (C28121)", - "opaqueid": "CQLD-C28121", - "owner.email": "sdat@microsoft.com", - "platform": "Desktop", - "precision": "medium", - "problem.severity": "warning", - "query-version": "v2", - "repro.text": "The following function call is taking place at an IRQL too high for what the call target is annotated as.", - "scope": "domainspecific", - "security.severity": "Low" - } - } + { + "tool": { + "driver": { + "name": "CodeQL", + "organization": "GitHub", + "semanticVersion": "2.24.2", + "notifications": [ + { + "id": "cpp/baseline/expected-extracted-files", + "name": "cpp/baseline/expected-extracted-files", + "shortDescription": { + "text": "Expected extracted files" + }, + "fullDescription": { + "text": "Files appearing in the source archive that are expected to be extracted." + }, + "defaultConfiguration": { + "enabled": true + }, + "properties": { + "tags": [ + "expected-extracted-files", + "telemetry" + ] + } + }, + { + "id": "cli/file-coverage-baseline", + "name": "cli/file-coverage-baseline", + "shortDescription": { + "text": "File coverage baseline telemetry" + }, + "fullDescription": { + "text": "File coverage baseline telemetry" + }, + "defaultConfiguration": { + "enabled": true + } + }, + { + "id": "cli/platform", + "name": "cli/platform", + "shortDescription": { + "text": "Platform" + }, + "fullDescription": { + "text": "Platform" + }, + "defaultConfiguration": { + "enabled": true + } + }, + { + "id": "cpp/extractor/summary", + "name": "cpp/extractor/summary", + "shortDescription": { + "text": "C++ extractor telemetry" + }, + "fullDescription": { + "text": "C++ extractor telemetry" + }, + "defaultConfiguration": { + "enabled": true + } + } + ], + "rules": [ + { + "id": "cpp/drivers/irql-too-high", + "name": "cpp/drivers/irql-too-high", + "shortDescription": { + "text": "IRQL too high (C28121)" + }, + "fullDescription": { + "text": "A function annotated with IRQL requirements was called at an IRQL too high for the requirements." + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning" + }, + "help": { + "text": "# IRQL too high (C28121)\nThe function is not permitted to be called at the current IRQ level. The current level is too high.\n\n\n## Recommendation\nThe driver is executing at an IRQL that is too high for the function that it is calling. Consult the WDK documentation for the function and verify the IRQL at which the function can be called. If you have applied custom IRQL annotations to your own functions, confirm that they are accurate.\n\n\n## Example\nIn this example, the driver is at too high of an IRQL to acquire a spinlock:\n\n```c\n\n\t\t\tNTSTATUS \n\t\t\tIrqlTooHigh(PKSPIN_LOCK myLock, PKIRQL oldIrql){\n\t\t\t\tNTSTATUS status;\n\t\t\t\tKIRQL lockIrql;\n\t\t\t\tKeRaiseIrql(HIGH_LEVEL, oldIrql);\n\t\t\t\tKeAcquireSpinLock(myLock, &lockIrql);\n\t\t\t\tKeReleaseSpinLock(myLock, &lockIrql);\n\t\t\t\tKeLowerIrql(*oldIrql);\n\t\t\t\treturn status;\n\t\t\t}\n\t\t\t\n\t\t\n```\nThe driver should be careful not to raise the IRQL too high:\n\n```c\n\n\t\t\tNTSTATUS \n\t\t\tIrqlTooHigh(PKSPIN_LOCK myLock, PKIRQL oldIrql){\n\t\t\t\tNTSTATUS status;\n\t\t\t\tKIRQL lockIrql;\n\t\t\t\tKeRaiseIrql(APC_LEVEL, oldIrql);\n\t\t\t\tKeAcquireSpinLock(myLock, &lockIrql);\n\t\t\t\tKeReleaseSpinLock(myLock, &lockIrql);\n\t\t\t\tKeLowerIrql(*oldIrql);\n\t\t\t\treturn status;\n\t\t\t}\n\t\t\t\n\t\t\n```\n\n## References\n* [ C28121 warning - Windows Drivers ](https://docs.microsoft.com/en-us/windows-hardware/drivers/devtest/28121-irq-execution-too-high)\n* [ IRQL annotations for drivers ](https://learn.microsoft.com/en-us/windows-hardware/drivers/devtest/irql-annotations-for-drivers)\n\n## Semmle-specific notes\nThis query uses interprocedural data-flow analysis and can take a large amount of CPU time and memory to run.\n\nThis query may provide false positives in cases where functions are not annotated with their expected IRQL ranges or behaviors.\n\nFor information on how to annotate your functions with information about how they adjust the IRQL, see \"IRQL annotations for drivers\" in the references section.\n\n**Dead-branch suppression.** The query suppresses calls inside an `if (b)` block when `b` is a non-`static` local variable initialized to `FALSE` / `0` that is never mutated (no `=`, no compound assignment, no `++` / `--`) and whose address is never taken. Compile-time constant `0` conditions are also suppressed. This is intended to silence dead-branch patterns produced by NDIS macros such as `FILTER_ACQUIRE_LOCK(lock, bFalse)`; the conditions on the variable case are deliberately conservative to avoid silently dropping legitimate findings when the runtime value of the variable cannot be proven to remain `FALSE` (globals reassigned in another function, address-taken locals mutated through a pointer, compound mutation, etc.).\n\n", + "markdown": "# IRQL too high (C28121)\nThe function is not permitted to be called at the current IRQ level. The current level is too high.\n\n\n## Recommendation\nThe driver is executing at an IRQL that is too high for the function that it is calling. Consult the WDK documentation for the function and verify the IRQL at which the function can be called. If you have applied custom IRQL annotations to your own functions, confirm that they are accurate.\n\n\n## Example\nIn this example, the driver is at too high of an IRQL to acquire a spinlock:\n\n```c\n\n\t\t\tNTSTATUS \n\t\t\tIrqlTooHigh(PKSPIN_LOCK myLock, PKIRQL oldIrql){\n\t\t\t\tNTSTATUS status;\n\t\t\t\tKIRQL lockIrql;\n\t\t\t\tKeRaiseIrql(HIGH_LEVEL, oldIrql);\n\t\t\t\tKeAcquireSpinLock(myLock, &lockIrql);\n\t\t\t\tKeReleaseSpinLock(myLock, &lockIrql);\n\t\t\t\tKeLowerIrql(*oldIrql);\n\t\t\t\treturn status;\n\t\t\t}\n\t\t\t\n\t\t\n```\nThe driver should be careful not to raise the IRQL too high:\n\n```c\n\n\t\t\tNTSTATUS \n\t\t\tIrqlTooHigh(PKSPIN_LOCK myLock, PKIRQL oldIrql){\n\t\t\t\tNTSTATUS status;\n\t\t\t\tKIRQL lockIrql;\n\t\t\t\tKeRaiseIrql(APC_LEVEL, oldIrql);\n\t\t\t\tKeAcquireSpinLock(myLock, &lockIrql);\n\t\t\t\tKeReleaseSpinLock(myLock, &lockIrql);\n\t\t\t\tKeLowerIrql(*oldIrql);\n\t\t\t\treturn status;\n\t\t\t}\n\t\t\t\n\t\t\n```\n\n## References\n* [ C28121 warning - Windows Drivers ](https://docs.microsoft.com/en-us/windows-hardware/drivers/devtest/28121-irq-execution-too-high)\n* [ IRQL annotations for drivers ](https://learn.microsoft.com/en-us/windows-hardware/drivers/devtest/irql-annotations-for-drivers)\n\n## Semmle-specific notes\nThis query uses interprocedural data-flow analysis and can take a large amount of CPU time and memory to run.\n\nThis query may provide false positives in cases where functions are not annotated with their expected IRQL ranges or behaviors.\n\nFor information on how to annotate your functions with information about how they adjust the IRQL, see \"IRQL annotations for drivers\" in the references section.\n\n**Dead-branch suppression.** The query suppresses calls inside an `if (b)` block when `b` is a non-`static` local variable initialized to `FALSE` / `0` that is never mutated (no `=`, no compound assignment, no `++` / `--`) and whose address is never taken. Compile-time constant `0` conditions are also suppressed. This is intended to silence dead-branch patterns produced by NDIS macros such as `FILTER_ACQUIRE_LOCK(lock, bFalse)`; the conditions on the variable case are deliberately conservative to avoid silently dropping legitimate findings when the runtime value of the variable cannot be proven to remain `FALSE` (globals reassigned in another function, address-taken locals mutated through a pointer, compound mutation, etc.).\n\n" + }, + "properties": { + "tags": [ + "correctness", + "ca_ported", + "wddst" + ], + "description": "A function annotated with IRQL requirements was called at an IRQL too high for the requirements.", + "feature.area": "Multiple", + "id": "cpp/drivers/irql-too-high", + "impact": "Exploitable Design", + "kind": "problem", + "name": "IRQL too high (C28121)", + "opaqueid": "CQLD-C28121", + "owner.email": "sdat@microsoft.com", + "platform": "Desktop", + "precision": "medium", + "problem.severity": "warning", + "query-version": "v5", + "repro.text": "The following function call is taking place at an IRQL too high for what the call target is annotated as.", + "scope": "domainspecific", + "security.severity": "Low" + } + } + ] + }, + "extensions": [ + { + "name": "microsoft/windows-drivers", + "semanticVersion": "1.9.0+a76f8551b47f01adada99ddc44a5ea4fa9839fca", + "locations": [ + { + "uri": "file:///F:/source/repos/Windows-Driver-Developer-Supplemental-Tools/src/", + "description": { + "text": "The QL pack root directory." + }, + "properties": { + "tags": [ + "CodeQL/LocalPackRoot" ] + } }, - "extensions": [ - { - "name": "microsoft/windows-drivers", - "semanticVersion": "1.2.0+ebba6989b75fe7ac336c358d0838781e7b17e5c2", - "locations": [ - { - "uri": "file:///C:/codeql-home/WDDST/src/", - "description": { - "text": "The QL pack root directory." - }, - "properties": { - "tags": [ - "CodeQL/LocalPackRoot" - ] - } - }, - { - "uri": "file:///C:/codeql-home/WDDST/src/qlpack.yml", - "description": { - "text": "The QL pack definition file." - }, - "properties": { - "tags": [ - "CodeQL/LocalPackDefinitionFile" - ] - } - } - ] - } - ] - }, - "invocations": [ { - "toolExecutionNotifications": [ - { - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "driver/driver_snippet.c", - "uriBaseId": "%SRCROOT%", - "index": 0 - } - } - } - ], - "message": { - "text": "" - }, - "level": "none", - "descriptor": { - "id": "cpp/baseline/expected-extracted-files", - "index": 0 - }, - "properties": { - "formattedMessage": { - "text": "" - } - } - }, - { - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "driver/fail_driver1.h", - "uriBaseId": "%SRCROOT%", - "index": 2 - } - } - } - ], - "message": { - "text": "" - }, - "level": "none", - "descriptor": { - "id": "cpp/baseline/expected-extracted-files", - "index": 0 - }, - "properties": { - "formattedMessage": { - "text": "" - } - } - }, - { - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "driver/fail_driver1.c", - "uriBaseId": "%SRCROOT%", - "index": 1 - } - } - } - ], - "message": { - "text": "" - }, - "level": "none", - "descriptor": { - "id": "cpp/baseline/expected-extracted-files", - "index": 0 - }, - "properties": { - "formattedMessage": { - "text": "" - } - } - }, - { - "message": { - "text": "Internal telemetry for the C++ extractor.\n\nNo action needed.", - "markdown": "Internal telemetry for the C++ extractor.\n\nNo action needed." - }, - "level": "note", - "timeUtc": "2024-10-16T03:27:35.793+00:00", - "descriptor": { - "id": "cpp/extractor/summary", - "index": 1 - }, - "properties": { - "attributes": { - "cache-hits": 0, - "cache-misses": 1, - "extractor-failures": 1, - "extractor-successes": 0, - "trap-caching": "disabled" - }, - "visibility": { - "statusPage": false, - "telemetry": true - } - } - } - ], - "executionSuccessful": true + "uri": "file:///F:/source/repos/Windows-Driver-Developer-Supplemental-Tools/src/qlpack.yml", + "description": { + "text": "The QL pack definition file." + }, + "properties": { + "tags": [ + "CodeQL/LocalPackDefinitionFile" + ] + } } - ], - "artifacts": [ + ] + }, + { + "name": "codeql/cpp-all", + "semanticVersion": "7.0.0+c5329f6f3863621c140ea7abd5954860e96c8bf1", + "locations": [ { - "location": { + "uri": "file:///C:/Users/natede/.codeql/packages/codeql/cpp-all/7.0.0/", + "description": { + "text": "The QL pack root directory." + }, + "properties": { + "tags": [ + "CodeQL/LocalPackRoot" + ] + } + }, + { + "uri": "file:///C:/Users/natede/.codeql/packages/codeql/cpp-all/7.0.0/qlpack.yml", + "description": { + "text": "The QL pack definition file." + }, + "properties": { + "tags": [ + "CodeQL/LocalPackDefinitionFile" + ] + } + } + ] + } + ] + }, + "invocations": [ + { + "toolExecutionNotifications": [ + { + "locations": [ + { + "physicalLocation": { + "artifactLocation": { "uri": "driver/driver_snippet.c", "uriBaseId": "%SRCROOT%", "index": 0 + } } + } + ], + "message": { + "text": "" }, - { - "location": { - "uri": "driver/fail_driver1.c", - "uriBaseId": "%SRCROOT%", - "index": 1 - } + "level": "none", + "descriptor": { + "id": "cpp/baseline/expected-extracted-files", + "index": 0 }, - { - "location": { + "properties": { + "formattedMessage": { + "text": "" + } + } + }, + { + "locations": [ + { + "physicalLocation": { + "artifactLocation": { "uri": "driver/fail_driver1.h", "uriBaseId": "%SRCROOT%", "index": 2 + } } + } + ], + "message": { + "text": "" + }, + "level": "none", + "descriptor": { + "id": "cpp/baseline/expected-extracted-files", + "index": 0 + }, + "properties": { + "formattedMessage": { + "text": "" + } } - ], - "results": [ - { - "ruleId": "cpp/drivers/irql-too-high", - "ruleIndex": 0, - "rule": { - "id": "cpp/drivers/irql-too-high", - "index": 0 - }, - "message": { - "text": "[TestInner1](1): IRQL potentially too high at call to [TestInner2](2). Maximum IRQL for this call: 1, IRQL at preceding node: 2" - }, - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "driver/driver_snippet.c", - "uriBaseId": "%SRCROOT%", - "index": 0 - }, - "region": { - "startLine": 42, - "startColumn": 12, - "endColumn": 22 - } - } - } - ], - "partialFingerprints": { - "primaryLocationLineHash": "1defbc9e59f0310b:1", - "primaryLocationStartColumnFingerprint": "7" - }, - "relatedLocations": [ - { - "id": 1, - "physicalLocation": { - "artifactLocation": { - "uri": "driver/driver_snippet.c", - "uriBaseId": "%SRCROOT%", - "index": 0 - }, - "region": { - "startLine": 41, - "startColumn": 10, - "endColumn": 20 - } - }, - "message": { - "text": "TestInner1" - } - }, - { - "id": 2, - "physicalLocation": { - "artifactLocation": { - "uri": "driver/driver_snippet.c", - "uriBaseId": "%SRCROOT%", - "index": 0 - }, - "region": { - "startLine": 42, - "startColumn": 12, - "endColumn": 22 - } - }, - "message": { - "text": "TestInner2" - } - } - ] + }, + { + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "driver/fail_driver1.c", + "uriBaseId": "%SRCROOT%", + "index": 1 + } + } + } + ], + "message": { + "text": "" }, - { - "ruleId": "cpp/drivers/irql-too-high", - "ruleIndex": 0, - "rule": { - "id": "cpp/drivers/irql-too-high", - "index": 0 - }, - "message": { - "text": "[TestInner2](1): IRQL potentially too high at call to [TestInner4](2). Maximum IRQL for this call: 0, IRQL at preceding node: 2" - }, - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "driver/driver_snippet.c", - "uriBaseId": "%SRCROOT%", - "index": 0 - }, - "region": { - "startLine": 36, - "startColumn": 14, - "endColumn": 24 - } - } - } - ], - "partialFingerprints": { - "primaryLocationLineHash": "7ae2af586e0dd70a:1", - "primaryLocationStartColumnFingerprint": "9" - }, - "relatedLocations": [ - { - "id": 1, - "physicalLocation": { - "artifactLocation": { - "uri": "driver/driver_snippet.c", - "uriBaseId": "%SRCROOT%", - "index": 0 - }, - "region": { - "startLine": 26, - "startColumn": 10, - "endColumn": 20 - } - }, - "message": { - "text": "TestInner2" - } - }, - { - "id": 2, - "physicalLocation": { - "artifactLocation": { - "uri": "driver/driver_snippet.c", - "uriBaseId": "%SRCROOT%", - "index": 0 - }, - "region": { - "startLine": 36, - "startColumn": 14, - "endColumn": 24 - } - }, - "message": { - "text": "TestInner4" - } - } - ] + "level": "none", + "descriptor": { + "id": "cpp/baseline/expected-extracted-files", + "index": 0 }, - { - "ruleId": "cpp/drivers/irql-too-high", - "ruleIndex": 0, - "rule": { - "id": "cpp/drivers/irql-too-high", - "index": 0 - }, - "message": { - "text": "[DpcForIsrRoutine](1): IRQL potentially too high at call to [IoGetInitialStack](2). Maximum IRQL for this call: 1, IRQL at preceding node: 2" - }, - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "driver/fail_driver1.c", - "uriBaseId": "%SRCROOT%", - "index": 1 - }, - "region": { - "startLine": 366, - "startColumn": 5, - "endColumn": 22 - } - } - } - ], - "partialFingerprints": { - "primaryLocationLineHash": "48e9dbeaff18e9e7:1", - "primaryLocationStartColumnFingerprint": "0" - }, - "relatedLocations": [ - { - "id": 1, - "physicalLocation": { - "artifactLocation": { - "uri": "driver/fail_driver1.c", - "uriBaseId": "%SRCROOT%", - "index": 1 - }, - "region": { - "startLine": 347, - "endColumn": 17 - } - }, - "message": { - "text": "DpcForIsrRoutine" - } - }, - { - "id": 2, - "physicalLocation": { - "artifactLocation": { - "uri": "driver/fail_driver1.c", - "uriBaseId": "%SRCROOT%", - "index": 1 - }, - "region": { - "startLine": 366, - "startColumn": 5, - "endColumn": 22 - } - }, - "message": { - "text": "IoGetInitialStack" - } - } - ] + "properties": { + "formattedMessage": { + "text": "" + } + } + }, + { + "message": { + "text": "" }, - { - "ruleId": "cpp/drivers/irql-too-high", - "ruleIndex": 0, - "rule": { - "id": "cpp/drivers/irql-too-high", - "index": 0 - }, - "message": { - "text": "[CompletionRoutine](1): IRQL potentially too high at call to [KeSetEvent](2). Maximum IRQL for this call: 1, IRQL at preceding node: 2" - }, - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "driver/fail_driver1.c", - "uriBaseId": "%SRCROOT%", - "index": 1 - }, - "region": { - "startLine": 324, - "startColumn": 5, - "endColumn": 15 - } - } - } + "level": "none", + "timeUtc": "2026-04-29T04:31:57.790909200Z", + "descriptor": { + "id": "cli/file-coverage-baseline", + "index": 1 + }, + "properties": { + "attributes": { + "durationMilliseconds": 317 + }, + "visibility": { + "statusPage": false, + "telemetry": true + } + } + }, + { + "message": { + "text": "" + }, + "level": "none", + "timeUtc": "2026-04-29T04:31:57.796908700Z", + "descriptor": { + "id": "cli/platform", + "index": 2 + }, + "properties": { + "attributes": { + "arch": "amd64", + "name": "Windows 11", + "version": "10.0" + }, + "visibility": { + "statusPage": false, + "telemetry": true + } + } + }, + { + "message": { + "text": "Internal telemetry for the C++ extractor.\n\nNo action needed.", + "markdown": "Internal telemetry for the C++ extractor.\n\nNo action needed." + }, + "level": "note", + "timeUtc": "2026-04-29T04:32:37.806676200Z", + "descriptor": { + "id": "cpp/extractor/summary", + "index": 3 + }, + "properties": { + "attributes": { + "cache-hits": 0, + "cache-misses": 1, + "compilers": [ + { + "program": "cl", + "version": "Microsoft (R) C/C++ Optimizing Compiler Version 19.50.35724 for x64" + } ], - "partialFingerprints": { - "primaryLocationLineHash": "779dfb1bf8eb10c3:1", - "primaryLocationStartColumnFingerprint": "0" - }, - "relatedLocations": [ - { - "id": 1, - "physicalLocation": { - "artifactLocation": { - "uri": "driver/fail_driver1.c", - "uriBaseId": "%SRCROOT%", - "index": 1 - }, - "region": { - "startLine": 303, - "endColumn": 18 - } - }, - "message": { - "text": "CompletionRoutine" - } - }, - { - "id": 2, - "physicalLocation": { - "artifactLocation": { - "uri": "driver/fail_driver1.c", - "uriBaseId": "%SRCROOT%", - "index": 1 - }, - "region": { - "startLine": 324, - "startColumn": 5, - "endColumn": 15 - } - }, - "message": { - "text": "KeSetEvent" - } - } - ] + "extractor-failures": 1, + "extractor-successes": 0, + "trap-caching": "disabled" + }, + "visibility": { + "statusPage": false, + "telemetry": true + } } + } ], - "columnKind": "utf16CodeUnits", - "properties": { - "semmle.formatSpecifier": "sarifv2.1.0" + "executionSuccessful": true + } + ], + "artifacts": [ + { + "location": { + "uri": "driver/driver_snippet.c", + "uriBaseId": "%SRCROOT%", + "index": 0 + } + }, + { + "location": { + "uri": "driver/fail_driver1.c", + "uriBaseId": "%SRCROOT%", + "index": 1 + } + }, + { + "location": { + "uri": "driver/fail_driver1.h", + "uriBaseId": "%SRCROOT%", + "index": 2 } + } + ], + "results": [ + { + "ruleId": "cpp/drivers/irql-too-high", + "ruleIndex": 0, + "rule": { + "id": "cpp/drivers/irql-too-high", + "index": 0 + }, + "message": { + "text": "[failForIrqlTooHigh_globalReassigned](1): IRQL potentially too high at call to [PassiveOnly_TooHigh](2). Maximum IRQL for this call: 0, IRQL at preceding node: 2" + }, + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "driver/driver_snippet.c", + "uriBaseId": "%SRCROOT%", + "index": 0 + }, + "region": { + "startLine": 150, + "startColumn": 9, + "endColumn": 28 + } + } + } + ], + "partialFingerprints": { + "primaryLocationLineHash": "b144babee8b922b:1", + "primaryLocationStartColumnFingerprint": "0" + }, + "relatedLocations": [ + { + "id": 1, + "physicalLocation": { + "artifactLocation": { + "uri": "driver/driver_snippet.c", + "uriBaseId": "%SRCROOT%", + "index": 0 + }, + "region": { + "startLine": 147, + "startColumn": 6, + "endColumn": 41 + } + }, + "message": { + "text": "failForIrqlTooHigh_globalReassigned" + } + }, + { + "id": 2, + "physicalLocation": { + "artifactLocation": { + "uri": "driver/driver_snippet.c", + "uriBaseId": "%SRCROOT%", + "index": 0 + }, + "region": { + "startLine": 150, + "startColumn": 9, + "endColumn": 28 + } + }, + "message": { + "text": "PassiveOnly_TooHigh" + } + } + ] + }, + { + "ruleId": "cpp/drivers/irql-too-high", + "ruleIndex": 0, + "rule": { + "id": "cpp/drivers/irql-too-high", + "index": 0 + }, + "message": { + "text": "[failForIrqlTooHigh_byReference](1): IRQL potentially too high at call to [PassiveOnly_TooHigh](2). Maximum IRQL for this call: 0, IRQL at preceding node: 2" + }, + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "driver/driver_snippet.c", + "uriBaseId": "%SRCROOT%", + "index": 0 + }, + "region": { + "startLine": 138, + "startColumn": 9, + "endColumn": 28 + } + } + } + ], + "partialFingerprints": { + "primaryLocationLineHash": "c860f155ed9fe979:1", + "primaryLocationStartColumnFingerprint": "0" + }, + "relatedLocations": [ + { + "id": 1, + "physicalLocation": { + "artifactLocation": { + "uri": "driver/driver_snippet.c", + "uriBaseId": "%SRCROOT%", + "index": 0 + }, + "region": { + "startLine": 133, + "startColumn": 6, + "endColumn": 36 + } + }, + "message": { + "text": "failForIrqlTooHigh_byReference" + } + }, + { + "id": 2, + "physicalLocation": { + "artifactLocation": { + "uri": "driver/driver_snippet.c", + "uriBaseId": "%SRCROOT%", + "index": 0 + }, + "region": { + "startLine": 138, + "startColumn": 9, + "endColumn": 28 + } + }, + "message": { + "text": "PassiveOnly_TooHigh" + } + } + ] + }, + { + "ruleId": "cpp/drivers/irql-too-high", + "ruleIndex": 0, + "rule": { + "id": "cpp/drivers/irql-too-high", + "index": 0 + }, + "message": { + "text": "[failForIrqlTooHigh_increment](1): IRQL potentially too high at call to [PassiveOnly_TooHigh](2). Maximum IRQL for this call: 0, IRQL at preceding node: 2" + }, + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "driver/driver_snippet.c", + "uriBaseId": "%SRCROOT%", + "index": 0 + }, + "region": { + "startLine": 126, + "startColumn": 9, + "endColumn": 28 + } + } + } + ], + "partialFingerprints": { + "primaryLocationLineHash": "bf3c3ea7c818ee5f:1", + "primaryLocationStartColumnFingerprint": "0" + }, + "relatedLocations": [ + { + "id": 1, + "physicalLocation": { + "artifactLocation": { + "uri": "driver/driver_snippet.c", + "uriBaseId": "%SRCROOT%", + "index": 0 + }, + "region": { + "startLine": 121, + "startColumn": 6, + "endColumn": 34 + } + }, + "message": { + "text": "failForIrqlTooHigh_increment" + } + }, + { + "id": 2, + "physicalLocation": { + "artifactLocation": { + "uri": "driver/driver_snippet.c", + "uriBaseId": "%SRCROOT%", + "index": 0 + }, + "region": { + "startLine": 126, + "startColumn": 9, + "endColumn": 28 + } + }, + "message": { + "text": "PassiveOnly_TooHigh" + } + } + ] + }, + { + "ruleId": "cpp/drivers/irql-too-high", + "ruleIndex": 0, + "rule": { + "id": "cpp/drivers/irql-too-high", + "index": 0 + }, + "message": { + "text": "[failForIrqlTooHigh_compoundAssignment](1): IRQL potentially too high at call to [PassiveOnly_TooHigh](2). Maximum IRQL for this call: 0, IRQL at preceding node: 2" + }, + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "driver/driver_snippet.c", + "uriBaseId": "%SRCROOT%", + "index": 0 + }, + "region": { + "startLine": 115, + "startColumn": 9, + "endColumn": 28 + } + } + } + ], + "partialFingerprints": { + "primaryLocationLineHash": "b8558f6fccea75ce:1", + "primaryLocationStartColumnFingerprint": "0" + }, + "relatedLocations": [ + { + "id": 1, + "physicalLocation": { + "artifactLocation": { + "uri": "driver/driver_snippet.c", + "uriBaseId": "%SRCROOT%", + "index": 0 + }, + "region": { + "startLine": 110, + "startColumn": 6, + "endColumn": 43 + } + }, + "message": { + "text": "failForIrqlTooHigh_compoundAssignment" + } + }, + { + "id": 2, + "physicalLocation": { + "artifactLocation": { + "uri": "driver/driver_snippet.c", + "uriBaseId": "%SRCROOT%", + "index": 0 + }, + "region": { + "startLine": 115, + "startColumn": 9, + "endColumn": 28 + } + }, + "message": { + "text": "PassiveOnly_TooHigh" + } + } + ] + }, + { + "ruleId": "cpp/drivers/irql-too-high", + "ruleIndex": 0, + "rule": { + "id": "cpp/drivers/irql-too-high", + "index": 0 + }, + "message": { + "text": "[TestInner1](1): IRQL potentially too high at call to [TestInner2](2). Maximum IRQL for this call: 1, IRQL at preceding node: 2" + }, + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "driver/driver_snippet.c", + "uriBaseId": "%SRCROOT%", + "index": 0 + }, + "region": { + "startLine": 42, + "startColumn": 12, + "endColumn": 22 + } + } + } + ], + "partialFingerprints": { + "primaryLocationLineHash": "1defbc9e59f0310b:1", + "primaryLocationStartColumnFingerprint": "7" + }, + "relatedLocations": [ + { + "id": 1, + "physicalLocation": { + "artifactLocation": { + "uri": "driver/driver_snippet.c", + "uriBaseId": "%SRCROOT%", + "index": 0 + }, + "region": { + "startLine": 41, + "startColumn": 10, + "endColumn": 20 + } + }, + "message": { + "text": "TestInner1" + } + }, + { + "id": 2, + "physicalLocation": { + "artifactLocation": { + "uri": "driver/driver_snippet.c", + "uriBaseId": "%SRCROOT%", + "index": 0 + }, + "region": { + "startLine": 42, + "startColumn": 12, + "endColumn": 22 + } + }, + "message": { + "text": "TestInner2" + } + } + ] + }, + { + "ruleId": "cpp/drivers/irql-too-high", + "ruleIndex": 0, + "rule": { + "id": "cpp/drivers/irql-too-high", + "index": 0 + }, + "message": { + "text": "[TestInner2](1): IRQL potentially too high at call to [TestInner4](2). Maximum IRQL for this call: 0, IRQL at preceding node: 2" + }, + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "driver/driver_snippet.c", + "uriBaseId": "%SRCROOT%", + "index": 0 + }, + "region": { + "startLine": 36, + "startColumn": 14, + "endColumn": 24 + } + } + } + ], + "partialFingerprints": { + "primaryLocationLineHash": "7ae2af586e0dd70a:1", + "primaryLocationStartColumnFingerprint": "9" + }, + "relatedLocations": [ + { + "id": 1, + "physicalLocation": { + "artifactLocation": { + "uri": "driver/driver_snippet.c", + "uriBaseId": "%SRCROOT%", + "index": 0 + }, + "region": { + "startLine": 26, + "startColumn": 10, + "endColumn": 20 + } + }, + "message": { + "text": "TestInner2" + } + }, + { + "id": 2, + "physicalLocation": { + "artifactLocation": { + "uri": "driver/driver_snippet.c", + "uriBaseId": "%SRCROOT%", + "index": 0 + }, + "region": { + "startLine": 36, + "startColumn": 14, + "endColumn": 24 + } + }, + "message": { + "text": "TestInner4" + } + } + ] + }, + { + "ruleId": "cpp/drivers/irql-too-high", + "ruleIndex": 0, + "rule": { + "id": "cpp/drivers/irql-too-high", + "index": 0 + }, + "message": { + "text": "[DpcForIsrRoutine](1): IRQL potentially too high at call to [IoGetInitialStack](2). Maximum IRQL for this call: 1, IRQL at preceding node: 2" + }, + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "driver/fail_driver1.c", + "uriBaseId": "%SRCROOT%", + "index": 1 + }, + "region": { + "startLine": 379, + "startColumn": 5, + "endColumn": 22 + } + } + } + ], + "partialFingerprints": { + "primaryLocationLineHash": "48e9dbeaff18e9e7:1", + "primaryLocationStartColumnFingerprint": "0" + }, + "relatedLocations": [ + { + "id": 1, + "physicalLocation": { + "artifactLocation": { + "uri": "driver/fail_driver1.c", + "uriBaseId": "%SRCROOT%", + "index": 1 + }, + "region": { + "startLine": 360, + "endColumn": 17 + } + }, + "message": { + "text": "DpcForIsrRoutine" + } + }, + { + "id": 2, + "physicalLocation": { + "artifactLocation": { + "uri": "driver/fail_driver1.c", + "uriBaseId": "%SRCROOT%", + "index": 1 + }, + "region": { + "startLine": 379, + "startColumn": 5, + "endColumn": 22 + } + }, + "message": { + "text": "IoGetInitialStack" + } + } + ] + }, + { + "ruleId": "cpp/drivers/irql-too-high", + "ruleIndex": 0, + "rule": { + "id": "cpp/drivers/irql-too-high", + "index": 0 + }, + "message": { + "text": "[CompletionRoutine](1): IRQL potentially too high at call to [KeSetEvent](2). Maximum IRQL for this call: 1, IRQL at preceding node: 2" + }, + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "driver/fail_driver1.c", + "uriBaseId": "%SRCROOT%", + "index": 1 + }, + "region": { + "startLine": 337, + "startColumn": 5, + "endColumn": 15 + } + } + } + ], + "partialFingerprints": { + "primaryLocationLineHash": "779dfb1bf8eb10c3:1", + "primaryLocationStartColumnFingerprint": "0" + }, + "relatedLocations": [ + { + "id": 1, + "physicalLocation": { + "artifactLocation": { + "uri": "driver/fail_driver1.c", + "uriBaseId": "%SRCROOT%", + "index": 1 + }, + "region": { + "startLine": 316, + "endColumn": 18 + } + }, + "message": { + "text": "CompletionRoutine" + } + }, + { + "id": 2, + "physicalLocation": { + "artifactLocation": { + "uri": "driver/fail_driver1.c", + "uriBaseId": "%SRCROOT%", + "index": 1 + }, + "region": { + "startLine": 337, + "startColumn": 5, + "endColumn": 15 + } + }, + "message": { + "text": "KeSetEvent" + } + } + ] + } + ], + "columnKind": "utf16CodeUnits", + "properties": { + "semmle.formatSpecifier": "sarifv2.1.0" } + } ] -} \ No newline at end of file +} diff --git a/src/drivers/general/queries/IrqlTooHigh/driver_snippet.c b/src/drivers/general/queries/IrqlTooHigh/driver_snippet.c index 6f498bcb..30976e53 100644 --- a/src/drivers/general/queries/IrqlTooHigh/driver_snippet.c +++ b/src/drivers/general/queries/IrqlTooHigh/driver_snippet.c @@ -73,4 +73,81 @@ passForIrqlTooHigh(PKIRQL oldIrql){ status = IrqlHighTestFunction(); KeLowerIrql(*oldIrql); return status; +} + +// ===================================================================== +// Adversarial cases for `isInConstantFalseBranch` (Irql.qll). +// +// These cases ensure the predicate that suppresses calls inside +// `if (b)` for variables that look "constantly FALSE" does not +// accidentally drop legitimate findings when `b` is in fact mutated +// at runtime via: +// - a compound assignment (|=, &=, +=, ...); +// - an increment / decrement (++, --); +// - a pass-by-reference helper that takes its address; +// - a separate function (when `b` is a file-scope global). +// +// Each adversarial case below should be flagged as IRQL-too-high. +// ===================================================================== + +_IRQL_requires_(PASSIVE_LEVEL) +NTSTATUS PassiveOnly_TooHigh(void){ + return STATUS_SUCCESS; +} + +static BOOLEAN g_DispatchSafe_TooHigh = FALSE; + +void initialize_global_dispatch_safe_TooHigh(void){ + g_DispatchSafe_TooHigh = TRUE; +} + +static void mutate_flag_by_pointer_TooHigh(PBOOLEAN pb){ + *pb = TRUE; +} + +// Adversarial: `bFalse |= 1` is `AssignBitwiseOrExpr`, which +// extends `AssignOperation` and not `AssignExpr`. +void failForIrqlTooHigh_compoundAssignment(PKIRQL oldIrql){ + BOOLEAN bFalse = FALSE; + bFalse |= 1; + KeRaiseIrql(DISPATCH_LEVEL, oldIrql); + if (bFalse) { + PassiveOnly_TooHigh(); + } + KeLowerIrql(*oldIrql); +} + +// Adversarial: `bFalse++` is `CrementOperation`. +void failForIrqlTooHigh_increment(PKIRQL oldIrql){ + BOOLEAN bFalse = FALSE; + bFalse++; + KeRaiseIrql(DISPATCH_LEVEL, oldIrql); + if (bFalse) { + PassiveOnly_TooHigh(); + } + KeLowerIrql(*oldIrql); +} + +// Adversarial: variable mutated by reference; no AssignExpr to +// `bFalse` exists in this function. +void failForIrqlTooHigh_byReference(PKIRQL oldIrql){ + BOOLEAN bFalse = FALSE; + mutate_flag_by_pointer_TooHigh(&bFalse); + KeRaiseIrql(DISPATCH_LEVEL, oldIrql); + if (bFalse) { + PassiveOnly_TooHigh(); + } + KeLowerIrql(*oldIrql); +} + +// Adversarial: file-scope global, reassigned only from other +// functions. A naive intra-function-only mutation check would +// fail to see the reassignment in the initialiser routine and +// silently drop the finding. +void failForIrqlTooHigh_globalReassigned(PKIRQL oldIrql){ + KeRaiseIrql(DISPATCH_LEVEL, oldIrql); + if (g_DispatchSafe_TooHigh) { + PassiveOnly_TooHigh(); + } + KeLowerIrql(*oldIrql); } \ No newline at end of file diff --git a/src/drivers/general/queries/IrqlTooLow/IrqlTooLow.md b/src/drivers/general/queries/IrqlTooLow/IrqlTooLow.md index 6b8dbad6..23fb2c74 100644 --- a/src/drivers/general/queries/IrqlTooLow/IrqlTooLow.md +++ b/src/drivers/general/queries/IrqlTooLow/IrqlTooLow.md @@ -43,3 +43,5 @@ This query may provide false positives in cases where functions are not annotate 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. +**Dead-branch suppression.** The query suppresses calls inside `if (b)` when `b` is either a compile-time constant `0`, or a non-`static` local initialized to `FALSE`/`0` that is never reassigned, incremented/decremented, or address-taken in the enclosing function. Targets dead-branch patterns from NDIS macros such as `FILTER_ACQUIRE_LOCK(lock, bFalse)`. The variable conditions are deliberately strict to avoid suppressing real findings on globals, function-statics, or values mutated through a pointer parameter. + diff --git a/src/drivers/general/queries/IrqlTooLow/IrqlTooLow.qhelp b/src/drivers/general/queries/IrqlTooLow/IrqlTooLow.qhelp index a505edbf..a62478d0 100644 --- a/src/drivers/general/queries/IrqlTooLow/IrqlTooLow.qhelp +++ b/src/drivers/general/queries/IrqlTooLow/IrqlTooLow.qhelp @@ -50,5 +50,19 @@

This query uses interprocedural data-flow analysis and can take a large amount of CPU time and memory to run.

This query may provide false positives in cases where functions are not annotated with their expected IRQL ranges or behaviors.

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.

+ +

+ Dead-branch suppression. The query suppresses calls + inside if (b) when b is either a + compile-time constant 0, or a + non-static local initialized to + FALSE/0 that is never reassigned, + incremented/decremented, or address-taken in the enclosing + function. Targets dead-branch patterns from NDIS macros such + as FILTER_ACQUIRE_LOCK(lock, bFalse). The + variable conditions are deliberately strict to avoid + suppressing real findings on globals, function-statics, or + values mutated through a pointer parameter. +

diff --git a/src/drivers/general/queries/IrqlTooLow/IrqlTooLow.ql b/src/drivers/general/queries/IrqlTooLow/IrqlTooLow.ql index e85c72a6..755f40e1 100644 --- a/src/drivers/general/queries/IrqlTooLow/IrqlTooLow.ql +++ b/src/drivers/general/queries/IrqlTooLow/IrqlTooLow.ql @@ -18,7 +18,7 @@ * ca_ported * wddst * @scope domainspecific - * @query-version v4 + * @query-version v6 */ import cpp @@ -35,7 +35,8 @@ where irqlRequirement = ifa.getIrqlLevel() and irqlRequirement != -1 and irqlRequirement > max(getPotentialExitIrqlAtCfn(prior)) and - not ifa.whenConditionIsFalseAtCallSite(call) + not ifa.whenConditionIsFalseAtCallSite(call) and + not isInConstantFalseBranch(call) select call, "$@: IRQL potentially too low at call to $@. Minimum IRQL for this call: " + irqlRequirement + ", IRQL at preceding node: " + max(getPotentialExitIrqlAtCfn(prior)), call.getControlFlowScope(), diff --git a/src/drivers/general/queries/IrqlTooLow/IrqlTooLow.sarif b/src/drivers/general/queries/IrqlTooLow/IrqlTooLow.sarif index d5b95a21..7ce307a5 100644 --- a/src/drivers/general/queries/IrqlTooLow/IrqlTooLow.sarif +++ b/src/drivers/general/queries/IrqlTooLow/IrqlTooLow.sarif @@ -7,7 +7,7 @@ "driver": { "name": "CodeQL", "organization": "GitHub", - "semanticVersion": "2.15.4", + "semanticVersion": "2.24.2", "notifications": [ { "id": "cpp/baseline/expected-extracted-files", @@ -27,6 +27,45 @@ "telemetry" ] } + }, + { + "id": "cli/file-coverage-baseline", + "name": "cli/file-coverage-baseline", + "shortDescription": { + "text": "File coverage baseline telemetry" + }, + "fullDescription": { + "text": "File coverage baseline telemetry" + }, + "defaultConfiguration": { + "enabled": true + } + }, + { + "id": "cli/platform", + "name": "cli/platform", + "shortDescription": { + "text": "Platform" + }, + "fullDescription": { + "text": "Platform" + }, + "defaultConfiguration": { + "enabled": true + } + }, + { + "id": "cpp/extractor/summary", + "name": "cpp/extractor/summary", + "shortDescription": { + "text": "C++ extractor telemetry" + }, + "fullDescription": { + "text": "C++ extractor telemetry" + }, + "defaultConfiguration": { + "enabled": true + } } ], "rules": [ @@ -43,9 +82,14 @@ "enabled": true, "level": "warning" }, + "help": { + "text": "# IRQL too low (C28120)\nThe function is not permitted to be called at the current IRQ level. The current level is too low.\n\n\n## Recommendation\nThe driver is executing at an IRQL that is too low for the function that it is calling. Consult the WDK documentation for the function and verify the IRQL at which the function can be called. If you have applied custom IRQL annotations to your own functions, confirm that they are accurate.\n\n\n## Example\nIn this example, the driver is calling a DDI that must be called at DISPATCH_LEVEL or higher:\n\n```c\n\n\t\t\t// Within a standard thread running at APC_LEVEL:\n\t\t\tif (KeShouldYieldProcessor())\n\t\t\t{\n\t\t\t\tKeLowerIrql(PASSIVE_LEVEL);\n\t\t\t}\n\t\t\t\n\t\t\n```\nThe driver should be careful to only call from a DISPATCH_LEVEL context:\n\n```c\n\n\t\t\t// Within a work loop running at DISPATCH_LEVEL\n\t\t\tif (KeShouldYieldProcessor())\n\t\t\t{\n\t\t\t\tKeLowerIrql(PASSIVE_LEVEL);\n\t\t\t}\n\t\t\t\n\t\t\n```\n\n## References\n* [ C28120 warning - Windows Drivers ](https://docs.microsoft.com/en-us/windows-hardware/drivers/devtest/28120-irql-execution-too-low)\n* [ IRQL annotations for drivers ](https://learn.microsoft.com/en-us/windows-hardware/drivers/devtest/irql-annotations-for-drivers)\n\n## Semmle-specific notes\nThis query uses interprocedural data-flow analysis and can take a large amount of CPU time and memory to run.\n\nThis query may provide false positives in cases where functions are not annotated with their expected IRQL ranges or behaviors.\n\nFor information on how to annotate your functions with information about how they adjust the IRQL, see \"IRQL annotations for drivers\" in the references section.\n\n**Dead-branch suppression.** The query suppresses calls inside an `if (b)` block when `b` is a non-`static` local variable initialized to `FALSE` / `0` that is never mutated (no `=`, no compound assignment, no `++` / `--`) and whose address is never taken. Compile-time constant `0` conditions are also suppressed. This is intended to silence dead-branch patterns produced by NDIS macros such as `FILTER_ACQUIRE_LOCK(lock, bFalse)`; the conditions on the variable case are deliberately conservative to avoid silently dropping legitimate findings when the runtime value of the variable cannot be proven to remain `FALSE` (globals reassigned in another function, address-taken locals mutated through a pointer, compound mutation, etc.).\n\n", + "markdown": "# IRQL too low (C28120)\nThe function is not permitted to be called at the current IRQ level. The current level is too low.\n\n\n## Recommendation\nThe driver is executing at an IRQL that is too low for the function that it is calling. Consult the WDK documentation for the function and verify the IRQL at which the function can be called. If you have applied custom IRQL annotations to your own functions, confirm that they are accurate.\n\n\n## Example\nIn this example, the driver is calling a DDI that must be called at DISPATCH_LEVEL or higher:\n\n```c\n\n\t\t\t// Within a standard thread running at APC_LEVEL:\n\t\t\tif (KeShouldYieldProcessor())\n\t\t\t{\n\t\t\t\tKeLowerIrql(PASSIVE_LEVEL);\n\t\t\t}\n\t\t\t\n\t\t\n```\nThe driver should be careful to only call from a DISPATCH_LEVEL context:\n\n```c\n\n\t\t\t// Within a work loop running at DISPATCH_LEVEL\n\t\t\tif (KeShouldYieldProcessor())\n\t\t\t{\n\t\t\t\tKeLowerIrql(PASSIVE_LEVEL);\n\t\t\t}\n\t\t\t\n\t\t\n```\n\n## References\n* [ C28120 warning - Windows Drivers ](https://docs.microsoft.com/en-us/windows-hardware/drivers/devtest/28120-irql-execution-too-low)\n* [ IRQL annotations for drivers ](https://learn.microsoft.com/en-us/windows-hardware/drivers/devtest/irql-annotations-for-drivers)\n\n## Semmle-specific notes\nThis query uses interprocedural data-flow analysis and can take a large amount of CPU time and memory to run.\n\nThis query may provide false positives in cases where functions are not annotated with their expected IRQL ranges or behaviors.\n\nFor information on how to annotate your functions with information about how they adjust the IRQL, see \"IRQL annotations for drivers\" in the references section.\n\n**Dead-branch suppression.** The query suppresses calls inside an `if (b)` block when `b` is a non-`static` local variable initialized to `FALSE` / `0` that is never mutated (no `=`, no compound assignment, no `++` / `--`) and whose address is never taken. Compile-time constant `0` conditions are also suppressed. This is intended to silence dead-branch patterns produced by NDIS macros such as `FILTER_ACQUIRE_LOCK(lock, bFalse)`; the conditions on the variable case are deliberately conservative to avoid silently dropping legitimate findings when the runtime value of the variable cannot be proven to remain `FALSE` (globals reassigned in another function, address-taken locals mutated through a pointer, compound mutation, etc.).\n\n" + }, "properties": { "tags": [ "correctness", + "ca_ported", "wddst" ], "description": "A function annotated with IRQL requirements was called at an IRQL too low for the requirements.", @@ -59,7 +103,7 @@ "platform": "Desktop", "precision": "medium", "problem.severity": "warning", - "query-version": "v2", + "query-version": "v6", "repro.text": "The following function call is taking place at an IRQL too low for what the call target is annotated as.", "scope": "domainspecific", "security.severity": "Low" @@ -70,18 +114,56 @@ "extensions": [ { "name": "microsoft/windows-drivers", - "semanticVersion": "1.1.0+2affc3c634804dac7504a483a378cc9ba22a0f0b", + "semanticVersion": "1.9.0+a76f8551b47f01adada99ddc44a5ea4fa9839fca", "locations": [ { - "uri": "file:///C:/codeql-home/Windows-Driver-Developer-Supplemental-Tools/src/", + "uri": "file:///F:/source/repos/Windows-Driver-Developer-Supplemental-Tools/src/", "description": { "text": "The QL pack root directory." + }, + "properties": { + "tags": [ + "CodeQL/LocalPackRoot" + ] } }, { - "uri": "file:///C:/codeql-home/Windows-Driver-Developer-Supplemental-Tools/src/qlpack.yml", + "uri": "file:///F:/source/repos/Windows-Driver-Developer-Supplemental-Tools/src/qlpack.yml", "description": { "text": "The QL pack definition file." + }, + "properties": { + "tags": [ + "CodeQL/LocalPackDefinitionFile" + ] + } + } + ] + }, + { + "name": "codeql/cpp-all", + "semanticVersion": "7.0.0+c5329f6f3863621c140ea7abd5954860e96c8bf1", + "locations": [ + { + "uri": "file:///C:/Users/natede/.codeql/packages/codeql/cpp-all/7.0.0/", + "description": { + "text": "The QL pack root directory." + }, + "properties": { + "tags": [ + "CodeQL/LocalPackRoot" + ] + } + }, + { + "uri": "file:///C:/Users/natede/.codeql/packages/codeql/cpp-all/7.0.0/qlpack.yml", + "description": { + "text": "The QL pack definition file." + }, + "properties": { + "tags": [ + "CodeQL/LocalPackDefinitionFile" + ] } } ] @@ -96,9 +178,9 @@ { "physicalLocation": { "artifactLocation": { - "uri": "driver/fail_driver1.c", + "uri": "driver/driver_snippet.c", "uriBaseId": "%SRCROOT%", - "index": 1 + "index": 0 } } } @@ -122,9 +204,9 @@ { "physicalLocation": { "artifactLocation": { - "uri": "driver/driver_snippet.c", + "uri": "driver/fail_driver1.h", "uriBaseId": "%SRCROOT%", - "index": 0 + "index": 1 } } } @@ -148,7 +230,7 @@ { "physicalLocation": { "artifactLocation": { - "uri": "driver/fail_driver1.h", + "uri": "driver/fail_driver1.c", "uriBaseId": "%SRCROOT%", "index": 2 } @@ -168,6 +250,79 @@ "text": "" } } + }, + { + "message": { + "text": "" + }, + "level": "none", + "timeUtc": "2026-04-29T04:35:27.144286700Z", + "descriptor": { + "id": "cli/file-coverage-baseline", + "index": 1 + }, + "properties": { + "attributes": { + "durationMilliseconds": 271 + }, + "visibility": { + "statusPage": false, + "telemetry": true + } + } + }, + { + "message": { + "text": "" + }, + "level": "none", + "timeUtc": "2026-04-29T04:35:27.147284600Z", + "descriptor": { + "id": "cli/platform", + "index": 2 + }, + "properties": { + "attributes": { + "arch": "amd64", + "name": "Windows 11", + "version": "10.0" + }, + "visibility": { + "statusPage": false, + "telemetry": true + } + } + }, + { + "message": { + "text": "Internal telemetry for the C++ extractor.\n\nNo action needed.", + "markdown": "Internal telemetry for the C++ extractor.\n\nNo action needed." + }, + "level": "note", + "timeUtc": "2026-04-29T04:36:08.306667Z", + "descriptor": { + "id": "cpp/extractor/summary", + "index": 3 + }, + "properties": { + "attributes": { + "cache-hits": 0, + "cache-misses": 1, + "compilers": [ + { + "program": "cl", + "version": "Microsoft (R) C/C++ Optimizing Compiler Version 19.50.35724 for x64" + } + ], + "extractor-failures": 1, + "extractor-successes": 0, + "trap-caching": "disabled" + }, + "visibility": { + "statusPage": false, + "telemetry": true + } + } } ], "executionSuccessful": true @@ -183,20 +338,296 @@ }, { "location": { - "uri": "driver/fail_driver1.c", + "uri": "driver/fail_driver1.h", "uriBaseId": "%SRCROOT%", "index": 1 } }, { "location": { - "uri": "driver/fail_driver1.h", + "uri": "driver/fail_driver1.c", "uriBaseId": "%SRCROOT%", "index": 2 } } ], "results": [ + { + "ruleId": "cpp/drivers/irql-too-low", + "ruleIndex": 0, + "rule": { + "id": "cpp/drivers/irql-too-low", + "index": 0 + }, + "message": { + "text": "[failForIrqlTooLow_globalReassigned](1): IRQL potentially too low at call to [DispatchOnly_TooLow](2). Minimum IRQL for this call: 2, IRQL at preceding node: 0" + }, + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "driver/driver_snippet.c", + "uriBaseId": "%SRCROOT%", + "index": 0 + }, + "region": { + "startLine": 112, + "startColumn": 9, + "endColumn": 28 + } + } + } + ], + "partialFingerprints": { + "primaryLocationLineHash": "4e9bf712603567a:1", + "primaryLocationStartColumnFingerprint": "0" + }, + "relatedLocations": [ + { + "id": 1, + "physicalLocation": { + "artifactLocation": { + "uri": "driver/driver_snippet.c", + "uriBaseId": "%SRCROOT%", + "index": 0 + }, + "region": { + "startLine": 110, + "startColumn": 6, + "endColumn": 40 + } + }, + "message": { + "text": "failForIrqlTooLow_globalReassigned" + } + }, + { + "id": 2, + "physicalLocation": { + "artifactLocation": { + "uri": "driver/driver_snippet.c", + "uriBaseId": "%SRCROOT%", + "index": 0 + }, + "region": { + "startLine": 112, + "startColumn": 9, + "endColumn": 28 + } + }, + "message": { + "text": "DispatchOnly_TooLow" + } + } + ] + }, + { + "ruleId": "cpp/drivers/irql-too-low", + "ruleIndex": 0, + "rule": { + "id": "cpp/drivers/irql-too-low", + "index": 0 + }, + "message": { + "text": "[failForIrqlTooLow_byReference](1): IRQL potentially too low at call to [DispatchOnly_TooLow](2). Minimum IRQL for this call: 2, IRQL at preceding node: 0" + }, + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "driver/driver_snippet.c", + "uriBaseId": "%SRCROOT%", + "index": 0 + }, + "region": { + "startLine": 105, + "startColumn": 9, + "endColumn": 28 + } + } + } + ], + "partialFingerprints": { + "primaryLocationLineHash": "30d5b4e4b89956a4:1", + "primaryLocationStartColumnFingerprint": "0" + }, + "relatedLocations": [ + { + "id": 1, + "physicalLocation": { + "artifactLocation": { + "uri": "driver/driver_snippet.c", + "uriBaseId": "%SRCROOT%", + "index": 0 + }, + "region": { + "startLine": 101, + "startColumn": 6, + "endColumn": 35 + } + }, + "message": { + "text": "failForIrqlTooLow_byReference" + } + }, + { + "id": 2, + "physicalLocation": { + "artifactLocation": { + "uri": "driver/driver_snippet.c", + "uriBaseId": "%SRCROOT%", + "index": 0 + }, + "region": { + "startLine": 105, + "startColumn": 9, + "endColumn": 28 + } + }, + "message": { + "text": "DispatchOnly_TooLow" + } + } + ] + }, + { + "ruleId": "cpp/drivers/irql-too-low", + "ruleIndex": 0, + "rule": { + "id": "cpp/drivers/irql-too-low", + "index": 0 + }, + "message": { + "text": "[failForIrqlTooLow_increment](1): IRQL potentially too low at call to [DispatchOnly_TooLow](2). Minimum IRQL for this call: 2, IRQL at preceding node: 0" + }, + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "driver/driver_snippet.c", + "uriBaseId": "%SRCROOT%", + "index": 0 + }, + "region": { + "startLine": 96, + "startColumn": 9, + "endColumn": 28 + } + } + } + ], + "partialFingerprints": { + "primaryLocationLineHash": "dd920dbcc12ad933:1", + "primaryLocationStartColumnFingerprint": "0" + }, + "relatedLocations": [ + { + "id": 1, + "physicalLocation": { + "artifactLocation": { + "uri": "driver/driver_snippet.c", + "uriBaseId": "%SRCROOT%", + "index": 0 + }, + "region": { + "startLine": 92, + "startColumn": 6, + "endColumn": 33 + } + }, + "message": { + "text": "failForIrqlTooLow_increment" + } + }, + { + "id": 2, + "physicalLocation": { + "artifactLocation": { + "uri": "driver/driver_snippet.c", + "uriBaseId": "%SRCROOT%", + "index": 0 + }, + "region": { + "startLine": 96, + "startColumn": 9, + "endColumn": 28 + } + }, + "message": { + "text": "DispatchOnly_TooLow" + } + } + ] + }, + { + "ruleId": "cpp/drivers/irql-too-low", + "ruleIndex": 0, + "rule": { + "id": "cpp/drivers/irql-too-low", + "index": 0 + }, + "message": { + "text": "[failForIrqlTooLow_compoundAssignment](1): IRQL potentially too low at call to [DispatchOnly_TooLow](2). Minimum IRQL for this call: 2, IRQL at preceding node: 0" + }, + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "driver/driver_snippet.c", + "uriBaseId": "%SRCROOT%", + "index": 0 + }, + "region": { + "startLine": 87, + "startColumn": 9, + "endColumn": 28 + } + } + } + ], + "partialFingerprints": { + "primaryLocationLineHash": "d830060d8a1428a7:1", + "primaryLocationStartColumnFingerprint": "0" + }, + "relatedLocations": [ + { + "id": 1, + "physicalLocation": { + "artifactLocation": { + "uri": "driver/driver_snippet.c", + "uriBaseId": "%SRCROOT%", + "index": 0 + }, + "region": { + "startLine": 83, + "startColumn": 6, + "endColumn": 42 + } + }, + "message": { + "text": "failForIrqlTooLow_compoundAssignment" + } + }, + { + "id": 2, + "physicalLocation": { + "artifactLocation": { + "uri": "driver/driver_snippet.c", + "uriBaseId": "%SRCROOT%", + "index": 0 + }, + "region": { + "startLine": 87, + "startColumn": 9, + "endColumn": 28 + } + }, + "message": { + "text": "DispatchOnly_TooLow" + } + } + ] + }, { "ruleId": "cpp/drivers/irql-too-low", "ruleIndex": 0, @@ -342,4 +773,4 @@ } } ] -} \ No newline at end of file +} diff --git a/src/drivers/general/queries/IrqlTooLow/driver_snippet.c b/src/drivers/general/queries/IrqlTooLow/driver_snippet.c index 6159d123..b133df8e 100644 --- a/src/drivers/general/queries/IrqlTooLow/driver_snippet.c +++ b/src/drivers/general/queries/IrqlTooLow/driver_snippet.c @@ -51,4 +51,64 @@ _IRQL_requires_(DISPATCH_LEVEL) NTSTATUS IrqlHighTestFunction(){ return STATUS_SUCCESS; +} + +// ===================================================================== +// Adversarial cases for `isInConstantFalseBranch` (Irql.qll). +// +// Symmetric to the IrqlTooHigh cases: the enclosing function is at +// PASSIVE_LEVEL but the call inside `if (b)` requires DISPATCH_LEVEL. +// Each case mutates `b` in a way that a naive constant-FALSE check +// can miss (compound assignment, increment, pass-by-reference, or a +// file-scope global reassigned in another function), so the predicate +// must not blindly suppress the inner call. +// ===================================================================== + +_IRQL_requires_(DISPATCH_LEVEL) +NTSTATUS DispatchOnly_TooLow(void){ + return STATUS_SUCCESS; +} + +static BOOLEAN g_DispatchSafe_TooLow = FALSE; + +void initialize_global_dispatch_safe_TooLow(void){ + g_DispatchSafe_TooLow = TRUE; +} + +static void mutate_flag_by_pointer_TooLow(PBOOLEAN pb){ + *pb = TRUE; +} + +_IRQL_requires_(PASSIVE_LEVEL) +void failForIrqlTooLow_compoundAssignment(void){ + BOOLEAN bFalse = FALSE; + bFalse |= 1; + if (bFalse) { + DispatchOnly_TooLow(); + } +} + +_IRQL_requires_(PASSIVE_LEVEL) +void failForIrqlTooLow_increment(void){ + BOOLEAN bFalse = FALSE; + bFalse++; + if (bFalse) { + DispatchOnly_TooLow(); + } +} + +_IRQL_requires_(PASSIVE_LEVEL) +void failForIrqlTooLow_byReference(void){ + BOOLEAN bFalse = FALSE; + mutate_flag_by_pointer_TooLow(&bFalse); + if (bFalse) { + DispatchOnly_TooLow(); + } +} + +_IRQL_requires_(PASSIVE_LEVEL) +void failForIrqlTooLow_globalReassigned(void){ + if (g_DispatchSafe_TooLow) { + DispatchOnly_TooLow(); + } } \ No newline at end of file diff --git a/src/drivers/libraries/Irql.qll b/src/drivers/libraries/Irql.qll index cb01e1be..34ce9267 100644 --- a/src/drivers/libraries/Irql.qll +++ b/src/drivers/libraries/Irql.qll @@ -235,6 +235,24 @@ // "Param == 0" is false when arg is nonzero paramName = cond.regexpCapture("(\\w+)\\s*==\\s*0", 1) and call.getArgument(paramIdx).getValue() != "0" + or + // "Param != NULL" is false when arg is 0/NULL + paramName = cond.regexpCapture("(\\w+)\\s*!=\\s*NULL", 1) and + call.getArgument(paramIdx).getValue() = "0" + or + // "((Param & 0x1)) 0" -- bitwise mask check. + // False when bit 0 is clear and op is "!=", or bit 0 is set and op is "==". + exists(string op, int argVal | + paramName = + cond.regexpCapture(".*\\(\\s*(\\w+)\\s*&\\s*0x1\\s*\\).*(==|!=)\\s*0.*", 1) and + op = cond.regexpCapture(".*\\(\\s*(\\w+)\\s*&\\s*0x1\\s*\\).*(==|!=)\\s*0.*", 2) and + argVal = call.getArgument(paramIdx).getValue().toInt() and + ( + op = "!=" and argVal.bitAnd(1) = 0 + or + op = "==" and argVal.bitAnd(1) != 0 + ) + ) ) ) } @@ -515,13 +533,13 @@ * "the IRQL before the corresponding save global call." */ int getIrqlLevel() { - result = any(getPotentialExitIrqlAtCfn(this.getMostRecentRaise().getAPredecessor())) + result = any(getPotentialExitIrqlAtCfnInternal(this.getMostRecentRaise().getAPredecessor())) } - + int getIrqlLevelExplicit() { result = any(getExplicitExitIrqlAtCfn(this.getMostRecentRaise().getAPredecessor())) } - + /** Returns the matching call to a function that saved the IRQL. */ IrqlSaveCall getMostRecentRaise() { result = @@ -595,7 +613,7 @@ */ int getIrqlLevel() { result = - any(getPotentialExitIrqlAtCfn(this.getMostRecentRaiseInterprocedural().getAPredecessor())) + any(getPotentialExitIrqlAtCfnInternal(this.getMostRecentRaiseInterprocedural().getAPredecessor())) } int getIrqlLevelExplicit() { @@ -646,13 +664,13 @@ * "the IRQL before the corresponding save global call." */ int getIrqlLevel() { - result = any(getPotentialExitIrqlAtCfn(this.getMostRecentRaise().getAPredecessor())) + result = any(getPotentialExitIrqlAtCfnInternal(this.getMostRecentRaise().getAPredecessor())) } - + int getIrqlLevelExplicit() { result = any(getExplicitExitIrqlAtCfn(this.getMostRecentRaise().getAPredecessor())) } - + /** * Returns the matching call to a function that saved the IRQL to a global state. * @@ -704,8 +722,46 @@ ) or not exists(Expr child | child = e1.getAChild() or child = e2.getAChild()) - } +} + /** Utility function to get all exit points of a function. */ + pragma[nomagic] + private ControlFlowNode getAnExitPointOfFunction(Function f) { + result.getControlFlowScope() = f and + functionExit(result) + } + + /** + * --- AI-generated --- + * + * Pre-computed summary: the potential exit IRQL of function `f`, + * computed once per function rather than re-discovered per call site. + * + * Calls the Internal cascade directly rather than the public wrapper + * so that this internal building block stays inside a single recursive + * stratum with the cascade. The wrapper layers an AST-level fallback + * over Internal using a negation, which would otherwise cycle back + * through here. Equivalent to the historical pre-wrapper behaviour + * because the wrapper agrees with Internal on every input where + * Internal binds. + */ + pragma[nomagic] + private int functionExitIrql(Function f) { + result = getPotentialExitIrqlAtCfnInternal(getAnExitPointOfFunction(f)) + } + + /** + * --- AI-generated --- + * + * Gets the set of predecessor nodes from callers for function `callee`. + * This pre-computes the reverse call-graph edge for interprocedural analysis + * and is restricted to actual call sites. + */ + pragma[nomagic] + private ControlFlowNode callerPredecessor(Function callee) { + result.getASuccessor().(FunctionCall).getTarget() = callee + } + /** * Attempt to provide the IRQL **once this control flow node exits**, based on annotations and direct calls to raising/lowering functions. * This predicate functions as follows: @@ -714,16 +770,42 @@ * - If calling a function annotated to restore the IRQL from a previously saved spot, then the result is the IRQL before that save call. * - If calling a function annotated to raise the IRQL, then it returns the annotated value (the target IRQL). * - If calling a function annotated to maintain the same IRQL, then the result is the IRQL at the previous CFN. - * - If this node is calling a function with no annotations, the result is the IRQL that function exits at. + * - If this node is calling a function with no annotations, the result is the IRQL that function exits at (pre-computed per function). * - If there is a prior CFN in the CFG, the result is the result for that prior CFN. * - If there is no prior CFN, then the result is whatever the IRQL was at a statement prior to a function call to this function (a lazy interprocedural analysis.) * - If there are no prior CFNs and no calls to this function, then the IRQL is determined by annotations applied to this function. * - Failing all this, we set the IRQL to 0. * + * --- AI-generated (layering note) --- + * + * Layering: this wraps the recursive cascade + * `getPotentialExitIrqlAtCfnInternal` with `astLevelExitIrqlFallback`, + * which fires only when the cascade binds nothing. The fallback walks + * one source-line step back to the nearest preceding sibling Stmt, and + * is restricted to CFNs inside loop bodies (the empirical failure + * mode for argument-expression CFNs reached via a back-edge). When the + * cascade already binds, this wrapper returns exactly the cascade's + * result set: the fallback never widens an existing binding. + * * Not implemented: _IRQL_limited_to_ */ - int getPotentialExitIrqlAtCfn(ControlFlowNode cfn) { + result = getPotentialExitIrqlAtCfnInternal(cfn) + or + not exists(getPotentialExitIrqlAtCfnInternal(cfn)) and + result = astLevelExitIrqlFallback(cfn) + } + + /** + * --- AI-generated --- + * + * Internal recursive cascade for `getPotentialExitIrqlAtCfn`. Behaves + * exactly like the historical `getPotentialExitIrqlAtCfn` did before + * the AST fallback wrapper was introduced. Callers should use + * `getPotentialExitIrqlAtCfn` instead, which additionally consults + * `astLevelExitIrqlFallback` when this cascade yields no value. + */ + private int getPotentialExitIrqlAtCfnInternal(ControlFlowNode cfn) { if cfn instanceof KeRaiseIrqlCall then result = cfn.(KeRaiseIrqlCall).getIrqlLevel() else @@ -744,33 +826,18 @@ if cfn instanceof FunctionCall and cfn.(FunctionCall).getTarget() instanceof IrqlRequiresSameAnnotatedFunction - then result = any(getPotentialExitIrqlAtCfn(cfn.getAPredecessor())) + then result = getPotentialExitIrqlAtCfnInternal(cfn.getAPredecessor()) else if cfn instanceof FunctionCall - then - result = - any(getPotentialExitIrqlAtCfn(getExitPointsOfFunction(cfn.(FunctionCall) - .getTarget())) - ) + then result = functionExitIrql(cfn.(FunctionCall).getTarget()) else if exists(ControlFlowNode cfn2 | cfn2 = cfn.getAPredecessor()) - then result = any(getPotentialExitIrqlAtCfn(cfn.getAPredecessor())) + then result = getPotentialExitIrqlAtCfnInternal(cfn.getAPredecessor()) else - if - exists(FunctionCall fc, ControlFlowNode cfn2 | - fc.getTarget() = cfn.getControlFlowScope() and - cfn2.getASuccessor() = fc - ) + if exists(callerPredecessor(cfn.getControlFlowScope())) then - // TODO: Check that this node is actually a function entry point and not just - // an isolated part of the CFN. Sometimes we get nodes that are "in" a function's - // CFN, but have no predecessors, but are not function entry. (Why?) result = - any(getPotentialExitIrqlAtCfn(any(ControlFlowNode cfn2 | - cfn2.getASuccessor().(FunctionCall).getTarget() = - cfn.getControlFlowScope() - )) - ) + getPotentialExitIrqlAtCfnInternal(callerPredecessor(cfn.getControlFlowScope())) else if cfn.getControlFlowScope() instanceof IrqlRestrictsFunction and @@ -778,13 +845,43 @@ then result = getAllowableIrqlLevel(cfn.getControlFlowScope()) else result = 0 } - - + + /** + * --- AI-generated --- + * + * AST-level fallback for `getPotentialExitIrqlAtCfn`. Returns the + * cascade's IRQL at the closest preceding sibling Stmt of `cfn`'s + * enclosing Stmt within the same parent. No value when there is no + * preceding sibling, or when the cascade is also silent there. + * + * Restricted to CFNs inside a loop body — that's the empirical + * failure mode where the cascade's predecessor walk doesn't bind on + * argument-expression CFNs reached via a loop back-edge. Outside + * loops, the cascade's silence often reflects branching the AST scan + * can't see (e.g. an if-stmt whose body raises and lowers IRQL), so + * we don't fall back there. + * + * Consulted by `getPotentialExitIrqlAtCfn` only when the cascade is + * silent; never widens an existing binding. + */ + private int astLevelExitIrqlFallback(ControlFlowNode cfn) { + exists(Stmt cfnStmt, Stmt prev, Loop l | + cfnStmt = cfn.getEnclosingStmt() and + cfnStmt.getParent+() = l and + prev.getParentStmt() = cfnStmt.getParentStmt() and + prev.getLocation().getStartLine() < cfnStmt.getLocation().getStartLine() and + not exists(Stmt closer | + closer.getParentStmt() = cfnStmt.getParentStmt() and + closer.getLocation().getStartLine() < cfnStmt.getLocation().getStartLine() and + closer.getLocation().getStartLine() > prev.getLocation().getStartLine() + ) and + result = getPotentialExitIrqlAtCfnInternal(prev) + ) + } + /* * Similar to above, but only exit points where the Irql is explicit */ - - int getExplicitExitIrqlAtCfn(ControlFlowNode cfn) { if cfn instanceof KeRaiseIrqlCall then result = cfn.(KeRaiseIrqlCall).getIrqlLevel() @@ -806,31 +903,16 @@ if cfn instanceof FunctionCall and cfn.(FunctionCall).getTarget() instanceof IrqlRequiresSameAnnotatedFunction - then result = any(getExplicitExitIrqlAtCfn(cfn.getAPredecessor())) + then result = getExplicitExitIrqlAtCfn(cfn.getAPredecessor()) else ( if exists(ControlFlowNode cfn2 | cfn2 = cfn.getAPredecessor()) - then result = any(getExplicitExitIrqlAtCfn(cfn.getAPredecessor())) + then result = getExplicitExitIrqlAtCfn(cfn.getAPredecessor()) else result = - any(getExplicitExitIrqlAtCfn(any(ControlFlowNode cfn2 | - cfn2.getASuccessor().(FunctionCall).getTarget() = - cfn.getControlFlowScope() - )) - ) + getExplicitExitIrqlAtCfn(callerPredecessor(cfn.getControlFlowScope())) ) } - - import semmle.code.cpp.controlflow.Dominance - - /** Utility function to get exit points of a function. */ - private ControlFlowNode getExitPointsOfFunction(Function f) { - result = - any(ControlFlowNode cfn | - cfn.getControlFlowScope() = f and - functionExit(cfn) - ) - } - + /** * Attempt to find the range of valid IRQL values when **entering** a given IRQL-annotated function. * This is used as a heuristic when no other IRQL information is available (i.e. we are at the top @@ -913,4 +995,90 @@ lowerBound = 0 ) } - \ No newline at end of file + + /** + * --- AI-generated --- + * + * Holds if `call` sits in the "then" branch of an `if` whose + * condition is either a compile-time-constant 0/FALSE, or a + * non-static local variable initialized to 0/FALSE that is never + * reassigned, incremented, or address-taken in the enclosing + * function. + * + * Detects e.g.: + * ``` + * BOOLEAN bFalse = FALSE; + * if (bFalse) { KeAcquireSpinLockAtDpcLevel(...); } // dead branch + * ``` + * Common in NDIS macros (FILTER_ACQUIRE_LOCK, NPROT_ACQUIRE_LOCK). + * + * The strict mutation checks (LocalVariable, !isStatic, Assignment + * matching `=` and compound ops, CrementOperation, no AddressOfExpr) + * exist so we don't suppress findings when the variable could + * plausibly hold a non-FALSE value at runtime — i.e. globals, + * function-statics, or values written through a pointer parameter. + */ + predicate isInConstantFalseBranch(FunctionCall call) { + exists(IfStmt ifStmt | + ifStmt.getThen().getAChild*() = call and + ( + // Condition is a literal 0 / false (or any compile-time constant 0). + ifStmt.getCondition().getValue() = "0" + or + // Condition is a variable access to a non-static local variable + // that is initialized to 0/FALSE and never mutated. + exists(LocalVariable v | + ifStmt.getCondition().(VariableAccess).getTarget() = v and + not v.isStatic() and + v.getInitializer().getExpr().getValue() = "0" and + not exists(Assignment a | + a.getLValue().(VariableAccess).getTarget() = v and + a.getEnclosingFunction() = call.getEnclosingFunction() + ) and + not exists(CrementOperation co | + co.getOperand().(VariableAccess).getTarget() = v and + co.getEnclosingFunction() = call.getEnclosingFunction() + ) and + not exists(AddressOfExpr ao | + ao.getOperand().(VariableAccess).getTarget() = v + ) + ) + ) + ) + } + /** + * --- AI-generated --- + * + * Holds if `fc` is a call that may change the IRQL: an IRQL primitive + * (`Ke*Irql*Call`, `*GlobalIrqlCall`), a function annotated + * `_IRQL_raises_` / `_IRQL_saves_global_` / `_IRQL_restores_global_`, + * or any function whose body transitively contains such a call. The + * transitive closure catches unannotated wrapper helpers. Lifted from + * IFSM.ql so other queries (e.g. IIWR) can use the same soundness check. + */ + predicate isIrqlChangingCall(FunctionCall fc) { + fc instanceof KeRaiseIrqlCall + or + fc instanceof KeLowerIrqlCall + or + fc instanceof RestoresGlobalIrqlCall + or + fc instanceof SavesGlobalIrqlCall + or + isIrqlChangingFunction(fc.getTarget()) + } + + /** + * --- AI-generated --- + * + * Holds if `f` directly or transitively contains an IRQL-changing call. + * See `isIrqlChangingCall`. + */ + predicate isIrqlChangingFunction(Function f) { + f instanceof IrqlChangesFunction + or + exists(FunctionCall inner | + inner.getEnclosingFunction() = f and + isIrqlChangingCall(inner) + ) + } \ No newline at end of file diff --git a/src/drivers/libraries/Page.qll b/src/drivers/libraries/Page.qll index 5584e2d4..a2f3c827 100644 --- a/src/drivers/libraries/Page.qll +++ b/src/drivers/libraries/Page.qll @@ -104,3 +104,41 @@ class PagedFunctionDeclaration extends Function { isAllocUsedToLocatePagedFunc(this) } } + +/** + * --- AI-generated --- + * + * A `PAGED_CODE` or `PAGED_CODE_LOCKED` macro invocation that sits inside + * a `PagedFunctionDeclaration`. Pre-filtering the population at the class + * level (rather than as joined `where`-clause predicates) lets the optimizer + * materialize a small relation and avoid the full + * `MacroInvocation x MacroInvocation` Cartesian product on large corpora. + * + * Routed through `getStmt()` (which always binds for `PAGED_CODE` / + * `PAGED_CODE_LOCKED`, since they expand to a stmt-form + * `NT_ASSERT_ASSUME`) to avoid the expensive `getAnAffectedElement` + * join used by the stock `MacroInvocation.getEnclosingFunction()`. + */ +class PagedCodeMacro extends MacroInvocation { + PagedCodeMacro() { + this.getMacroName() = ["PAGED_CODE", "PAGED_CODE_LOCKED"] + and this.getStmt().getEnclosingFunction() instanceof PagedFunctionDeclaration + } + + /** + * --- AI-generated --- + * + * Gets the paged enclosing function for this macro invocation, + * including template instantiations. + * + * NB: to compare two `PagedCodeMacro` invocations for "same + * source-level function", also require `getFile()` agreement — + * the extractor sometimes consolidates ODR-equivalent template + * definitions across headers into a single `Function` entity, which + * would otherwise allow a macro in one header to match an enclosing + * function in another. + */ + Function getEnclosingPagedFunction() { + result = this.getStmt().getEnclosingFunction() + } +} diff --git a/src/drivers/libraries/Suppression.qll b/src/drivers/libraries/Suppression.qll index c48fb176..6a3231ea 100644 --- a/src/drivers/libraries/Suppression.qll +++ b/src/drivers/libraries/Suppression.qll @@ -2,6 +2,44 @@ // Licensed under the MIT license. import cpp +/** + * --- AI-generated --- + * + * Holds if `d` is a DisablePragma that falls within SuppressionPushPopSegment `s`. + */ +pragma[nomagic] +private predicate disableInSegment(DisablePragma d, SuppressionPushPopSegment s) { + d.getFile() = s.getFile() and + d.getLocation().getStartLine() >= s.getSegmentStartLine() and + d.getLocation().getEndLine() <= s.getSegmentEndLine() +} + +/** + * --- AI-generated --- + * + * Holds if `d` is a DisablePragma that is inside at least one push/pop segment. + */ +pragma[nomagic] +private predicate disableHasSegment(DisablePragma d) { + exists(SuppressionPushPopSegment s | disableInSegment(d, s)) +} + +/** + * --- AI-generated --- + * + * Holds if a Location in file `f` spanning `startLine` to `endLine` + * falls inside at least one push/pop segment. + */ +bindingset[f, startLine, endLine] +pragma[inline_late] +private predicate locationInAnySegment(File f, int startLine, int endLine) { + exists(SuppressionPushPopSegment s | + f = s.getFile() and + startLine >= s.getSegmentStartLine() and + endLine <= s.getSegmentEndLine() + ) +} + // Reference: https://learn.microsoft.com/en-us/cpp/preprocessor/warning?view=msvc-170 /** * Represents a Code Analysis-style suppression using #pragma commands. @@ -23,6 +61,15 @@ abstract class CASuppression extends PreprocessorPragma { /** Evaluates if the given location is suppressed by this suppression. */ abstract predicate appliesToLocation(Location l); + /** + * --- AI-generated --- + * + * Gets the scope of this suppression as a line range within a file. + * This is used by `hasLocationInfo` to define where the suppression applies + * without enumerating every Location in the database. + */ + abstract predicate scopeCovers(File f, int startLine, int endLine); + /** Returns the scope covered by this suppression. */ CASuppressionScope getScope() { result = this } @@ -119,18 +166,23 @@ abstract class CASuppression extends PreprocessorPragma { } } -/** Represents the scope covered by a given CA supression. */ +/** Represents the scope covered by a given CA suppression. */ class CASuppressionScope extends ElementBase instanceof CASuppression { + /** + * --- AI-generated --- + * + * Defines the location range covered by this suppression scope. + * Instead of iterating all Location objects, this uses pre-computed scope bounds + * to return the bounding box of the suppression region directly. + */ predicate hasLocationInfo( string filepath, int startline, int startcolumn, int endline, int endcolumn ) { - exists(Location l | - l.getFile().getAbsolutePath() = filepath and - l.getStartLine() = startline and - l.getStartColumn() = startcolumn and - l.getEndLine() = endline and - l.getEndColumn() = endcolumn and - super.appliesToLocation(l) + exists(File f | + super.scopeCovers(f, startline, endline) and + filepath = f.getAbsolutePath() and + startcolumn = 1 and + endcolumn = 1 ) } } @@ -171,18 +223,39 @@ class SuppressPragma extends CASuppression { this.getLocation().getEndLine() + this.getMinimumLocationOffset() = l.getStartLine() } + /** The scope of a suppress pragma is just the single next code line. */ + pragma[nomagic] + override predicate scopeCovers(File f, int startLine, int endLine) { + f = this.getFile() and + startLine = this.getLocation().getEndLine() + this.getMinimumLocationOffset() and + endLine = startLine + } + /** Finds the offset (in line count) to the closest non-pragma element after this suppression. */ pragma[nomagic] - int getMinimumLocationOffset() { + exists(int nextLine | + nextLine = nextNonSuppressionLine() and + result = nextLine - this.getLocation().getEndLine() + ) + } + + /** + * --- AI-generated --- + * + * Gets the minimum start line of a non-suppression Locatable in `f` that is + * strictly after `afterLine`. Pre-computing distinct lines avoids iterating + * over every Locatable individually in the aggregate. + */ + pragma[nomagic] + private int nextNonSuppressionLine() { result = - min(int i | - i > 0 and + min(int line | exists(Locatable l | l.getFile() = this.getFile() and - l.getLocation().getStartLine() > this.getLocation().getEndLine() and not l instanceof CASuppression and - this.getLocation().getEndLine() + i = l.getLocation().getStartLine() + line = l.getLocation().getStartLine() and + line > this.getLocation().getEndLine() ) ) } @@ -219,12 +292,36 @@ class DisablePragma extends CASuppression { // If we're in a pragma push/pop, ensure the disable is too ( exists(SuppressionPushPopSegment spps | - spps.getADisablePragma() = this and + disableInSegment(this, spps) and spps.isInPushPopSegment(l) ) or - not exists(SuppressionPushPopSegment spps | spps.getADisablePragma() = this) and - not exists(SuppressionPushPopSegment spps | spps.isInPushPopSegment(l)) + not disableHasSegment(this) and + not locationInAnySegment(l.getFile(), l.getStartLine(), l.getEndLine()) + ) + } + + /** + * --- AI-generated --- + * + * The scope of a disable pragma: from the disable line to either the end of the + * enclosing push/pop segment, or the end of the file. + * Returns a single bounding range rather than enumerating every Location. + */ + pragma[nomagic] + override predicate scopeCovers(File f, int startLine, int endLine) { + f = this.getFile() and + startLine = this.getLocation().getEndLine() and + ( + // If in a push/pop segment, scope ends at the segment end + exists(SuppressionPushPopSegment spps | + disableInSegment(this, spps) and + endLine = spps.getSegmentEndLine() + ) + or + // If not in any segment, scope covers to end of file + not disableHasSegment(this) and + endLine = max(int line | exists(Location l | l.getFile() = f and line = l.getEndLine())) ) } } @@ -277,8 +374,14 @@ class SuppressionPushPopSegment extends Location { ) } + /** Gets the end line of the push (start of the segment content). */ + int getSegmentStartLine() { result = start.getEndLine() } + + /** Gets the start line of the pop (end of the segment content). */ + int getSegmentEndLine() { result = end.getStartLine() } + /** Determines if a given location is in this push/pop segment. */ - pragma[inline] + pragma[nomagic] predicate isInPushPopSegment(Location l) { l.getFile() = this.getFile() and l.getStartLine() >= start.getEndLine() and @@ -286,8 +389,7 @@ class SuppressionPushPopSegment extends Location { } /** Returns a disable pragma within this push/pop segment. */ - DisablePragma getADisablePragma() { - result = any(DisablePragma p | this.isInPushPopSegment(p.getLocation())) + disableInSegment(result, this) } } diff --git a/src/drivers/wdm/queries/MultiplePagedCode/MultiplePagedCode.md b/src/drivers/wdm/queries/MultiplePagedCode/MultiplePagedCode.md index 2b561625..923ae1f1 100644 --- a/src/drivers/wdm/queries/MultiplePagedCode/MultiplePagedCode.md +++ b/src/drivers/wdm/queries/MultiplePagedCode/MultiplePagedCode.md @@ -13,7 +13,7 @@ Remove all but one PAGED_CODE OR PAGED_CODE_LOCKED macro. // Licensed under the MIT license. -//Macros to enable or disable a code section that may or maynot conflict with this test. +// Macros to enable or disable a code section that may or may not conflict with this test. #define SET_DISPATCH 1 #define SET_PAGE_CODE 1 @@ -72,3 +72,7 @@ DispatchShutdown ( ## References * [ C28171 warning - Windows Drivers ](https://docs.microsoft.com/en-us/windows-hardware/drivers/devtest/28171-function-has-more-than-one-page-macro-instance) + +## Semmle-specific notes +**C++ function template support.** When the same source-level `PAGED_CODE` is expanded into multiple template instantiations, the query collapses all instantiations into a single match key (via the underlying `TemplateFunction`) so a duplicate inside a templated paged function body is reported once at the source-level location. Specialisations and non-template paged functions are excluded. + diff --git a/src/drivers/wdm/queries/MultiplePagedCode/MultiplePagedCode.qhelp b/src/drivers/wdm/queries/MultiplePagedCode/MultiplePagedCode.qhelp index 70fdef92..502c7025 100644 --- a/src/drivers/wdm/queries/MultiplePagedCode/MultiplePagedCode.qhelp +++ b/src/drivers/wdm/queries/MultiplePagedCode/MultiplePagedCode.qhelp @@ -21,4 +21,17 @@ + + +

+ C++ function template support. When the same + source-level PAGED_CODE is expanded into multiple + template instantiations, the query collapses all + instantiations into a single match key (via the underlying + TemplateFunction) so a duplicate inside a + templated paged function body is reported once at the + source-level location. Specialisations and non-template + paged functions are excluded. +

+
diff --git a/src/drivers/wdm/queries/MultiplePagedCode/MultiplePagedCode.ql b/src/drivers/wdm/queries/MultiplePagedCode/MultiplePagedCode.ql index 202c4e3f..4b1c4504 100644 --- a/src/drivers/wdm/queries/MultiplePagedCode/MultiplePagedCode.ql +++ b/src/drivers/wdm/queries/MultiplePagedCode/MultiplePagedCode.ql @@ -18,19 +18,38 @@ * ca_ported * wddst * @scope domainspecific - * @query-version v1 + * @query-version v4 */ import cpp import drivers.libraries.Page -// Selects functions that have at least two instances of a PAGED_CODE macro. -from MacroInvocation mi, MacroInvocation mi2 +// Selects functions that have at least two PAGED_CODE/PAGED_CODE_LOCKED +// macro invocations on distinct source lines. +// +// Implementation note: ranges over the pre-filtered `PagedCodeMacro` class +// and uses `getEnclosingPagedFunction()` (defined in `Page.qll`), which +// routes through `MacroInvocation.getStmt().getEnclosingFunction()`. The +// stock `MacroInvocation.getEnclosingFunction()` is built on +// `getAnAffectedElement()` and materializes a relation that scales poorly +// on large codebases, causing analysis timeouts. `getStmt()` uses only the +// cheaper `inmacroexpansion` relation and returns the unique outermost +// `Stmt`. +// +// The same-file constraint on `mi`, `mi2` and `f` defends against ODR- +// equivalent template entities that the cpp extractor sometimes +// consolidates across headers (e.g. two driver-private headers each +// defining `template Foo()`); without the constraint, the inner +// `mi` could match a macro invocation in a sibling header that happens to +// share the consolidated `Function` entity. +from PagedCodeMacro mi2, Function f where - mi.getEnclosingFunction() = mi2.getEnclosingFunction() and - mi.getEnclosingFunction() instanceof PagedFunctionDeclaration and - mi.getMacroName() = ["PAGED_CODE", "PAGED_CODE_LOCKED"] and - mi2.getMacroName() = ["PAGED_CODE", "PAGED_CODE_LOCKED"] and - mi.getLocation().getStartLine() < mi2.getLocation().getStartLine() + f = mi2.getEnclosingPagedFunction() and + f.getFile() = mi2.getFile() and + exists(PagedCodeMacro mi | + mi.getEnclosingPagedFunction() = f and + mi.getFile() = mi2.getFile() and + mi.getLocation().getStartLine() < mi2.getLocation().getStartLine() + ) select mi2, "Functions in a paged section must have exactly one instance of the PAGED_CODE or PAGED_CODE_LOCKED macro" diff --git a/src/drivers/wdm/queries/MultiplePagedCode/MultiplePagedCode.sarif b/src/drivers/wdm/queries/MultiplePagedCode/MultiplePagedCode.sarif index 41a84ed4..94f41707 100644 --- a/src/drivers/wdm/queries/MultiplePagedCode/MultiplePagedCode.sarif +++ b/src/drivers/wdm/queries/MultiplePagedCode/MultiplePagedCode.sarif @@ -1,114 +1,393 @@ -{ - "$schema" : "https://json.schemastore.org/sarif-2.1.0.json", - "version" : "2.1.0", - "runs" : [ { - "tool" : { - "driver" : { - "name" : "CodeQL", - "organization" : "GitHub", - "semanticVersion" : "2.11.5", - "rules" : [ { - "id" : "cpp/drivers/multiple-paged-code", - "name" : "cpp/drivers/multiple-paged-code", - "shortDescription" : { - "text" : "Multiple instances of PAGED_CODE or PAGED_CODE_LOCKED" - }, - "fullDescription" : { - "text" : "The function has more than one instance of PAGED_CODE or PAGED_CODE_LOCKED. This can cause issues when debugging, using Code Analysis, or running on checked builds." - }, - "defaultConfiguration" : { - "enabled" : true, - "level" : "warning" - }, - "properties" : { - "tags" : [ "correctness", "wddst" ], - "description" : "The function has more than one instance of PAGED_CODE or PAGED_CODE_LOCKED. This can cause issues when debugging, using Code Analysis, or running on checked builds.", - "feature.area" : "Multiple", - "id" : "cpp/drivers/multiple-paged-code", - "impact" : "Insecure Coding Practice", - "kind" : "problem", - "name" : "Multiple instances of PAGED_CODE or PAGED_CODE_LOCKED", - "opaqueid" : "CQLD-C28171", - "owner.email" : "sdat@microsoft.com", - "platform" : "Desktop", - "precision" : "high", - "problem.severity" : "warning", - "query-version" : "v1", - "repro.text" : "The following code locations are duplicate PAGED_CODE() calls within a function.", - "scope" : "domainspecific", - "security.severity" : "Low" - } - } ] - }, - "extensions" : [ { - "name" : "microsoft/windows-drivers", - "semanticVersion" : "0.1.0+933e876f096a70922173e4d5ad604d99d4481af4", - "locations" : [ { - "uri" : "file:///C:/codeql-home/Windows-Driver-Developer-Supplemental-Tools/src/", - "description" : { - "text" : "The QL pack root directory." - } - }, { - "uri" : "file:///C:/codeql-home/Windows-Driver-Developer-Supplemental-Tools/src/qlpack.yml", - "description" : { - "text" : "The QL pack definition file." - } - } ] - }, { - "name" : "legacy-upgrades", - "semanticVersion" : "0.0.0", - "locations" : [ { - "uri" : "file:///C:/codeql-home/codeql/legacy-upgrades/", - "description" : { - "text" : "The QL pack root directory." - } - }, { - "uri" : "file:///C:/codeql-home/codeql/legacy-upgrades/qlpack.yml", - "description" : { - "text" : "The QL pack definition file." - } - } ] - } ] - }, - "artifacts" : [ { - "location" : { - "uri" : "driver/driver_snippet.c", - "uriBaseId" : "%SRCROOT%", - "index" : 0 - } - } ], - "results" : [ { - "ruleId" : "cpp/drivers/multiple-paged-code", - "ruleIndex" : 0, - "rule" : { - "id" : "cpp/drivers/multiple-paged-code", - "index" : 0 - }, - "message" : { - "text" : "Functions in a paged section must have exactly one instance of the PAGED_CODE or PAGED_CODE_LOCKED macro" - }, - "locations" : [ { - "physicalLocation" : { - "artifactLocation" : { - "uri" : "driver/driver_snippet.c", - "uriBaseId" : "%SRCROOT%", - "index" : 0 - }, - "region" : { - "startLine" : 50, - "startColumn" : 5, - "endColumn" : 17 - } - } - } ], - "partialFingerprints" : { - "primaryLocationLineHash" : "c7556935fb8cd898:1", - "primaryLocationStartColumnFingerprint" : "0" - } - } ], - "columnKind" : "utf16CodeUnits", - "properties" : { - "semmle.formatSpecifier" : "sarifv2.1.0" - } - } ] -} \ No newline at end of file +{ + "$schema": "https://json.schemastore.org/sarif-2.1.0.json", + "version": "2.1.0", + "runs": [ + { + "tool": { + "driver": { + "name": "CodeQL", + "organization": "GitHub", + "semanticVersion": "2.24.2", + "notifications": [ + { + "id": "cpp/baseline/expected-extracted-files", + "name": "cpp/baseline/expected-extracted-files", + "shortDescription": { + "text": "Expected extracted files" + }, + "fullDescription": { + "text": "Files appearing in the source archive that are expected to be extracted." + }, + "defaultConfiguration": { + "enabled": true + }, + "properties": { + "tags": [ + "expected-extracted-files", + "telemetry" + ] + } + }, + { + "id": "cli/file-coverage-baseline", + "name": "cli/file-coverage-baseline", + "shortDescription": { + "text": "File coverage baseline telemetry" + }, + "fullDescription": { + "text": "File coverage baseline telemetry" + }, + "defaultConfiguration": { + "enabled": true + } + }, + { + "id": "cli/platform", + "name": "cli/platform", + "shortDescription": { + "text": "Platform" + }, + "fullDescription": { + "text": "Platform" + }, + "defaultConfiguration": { + "enabled": true + } + }, + { + "id": "cpp/extractor/summary", + "name": "cpp/extractor/summary", + "shortDescription": { + "text": "C++ extractor telemetry" + }, + "fullDescription": { + "text": "C++ extractor telemetry" + }, + "defaultConfiguration": { + "enabled": true + } + } + ], + "rules": [ + { + "id": "cpp/drivers/multiple-paged-code", + "name": "cpp/drivers/multiple-paged-code", + "shortDescription": { + "text": "Multiple instances of PAGED_CODE or PAGED_CODE_LOCKED" + }, + "fullDescription": { + "text": "The function has more than one instance of PAGED_CODE or PAGED_CODE_LOCKED. This can cause issues when debugging, using Code Analysis, or running on checked builds." + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning" + }, + "help": { + "text": "# Multiple instances of PAGED_CODE or PAGED_CODE_LOCKED\nThe function has more than one instance of PAGED_CODE or PAGED_CODE_LOCKED. This warning indicates that there is more than one instance of the PAGED_CODE or PAGED_CODE_LOCKED macro in a function. This error is reported at the second or subsequent instances of the PAGED_CODE or PAGED_CODE_LOCKED macro.\n\n\n## Recommendation\nRemove all but one PAGED_CODE OR PAGED_CODE_LOCKED macro.\n\n\n## Example\n\n```c\n// Copyright (c) Microsoft Corporation.\r\n// Licensed under the MIT license.\r\n\r\n\r\n//Macros to enable or disable a code section that may or maynot conflict with this test.\r\n#define SET_DISPATCH 1\r\n#define SET_PAGE_CODE 1\r\n\r\n\r\n_Dispatch_type_(IRP_MJ_CLEANUP) \r\nDRIVER_DISPATCH DispatchCleanup;\r\n\r\n_Dispatch_type_(IRP_MJ_SHUTDOWN)\r\nDRIVER_DISPATCH DispatchShutdown;\r\n\r\n#ifndef __cplusplus\r\n#pragma alloc_text (PAGE, DispatchCleanup)\r\n#pragma alloc_text (PAGE, DispatchShutdown)\r\n#endif\r\n\r\n\r\n//Template\r\nvoid top_level_call(){\r\n}\r\n\r\n//Passes\r\nNTSTATUS\r\nDispatchCleanup (\r\n PDEVICE_OBJECT DriverObject,\r\n PIRP Irp\r\n )\r\n{\r\n UNREFERENCED_PARAMETER(DriverObject);\r\n UNREFERENCED_PARAMETER(Irp);\r\n PAGED_CODE();\r\n \r\n return STATUS_SUCCESS;\r\n}\r\n\r\n//Fails\r\nNTSTATUS\r\nDispatchShutdown (\r\n PDEVICE_OBJECT DriverObject,\r\n PIRP Irp\r\n )\r\n{\r\n UNREFERENCED_PARAMETER(DriverObject);\r\n UNREFERENCED_PARAMETER(Irp);\r\n PAGED_CODE();\r\n PAGED_CODE();\r\n \r\n return STATUS_SUCCESS;\r\n}\r\n\r\n\r\n\r\n\r\n\r\n\n```\n\n## References\n* [ C28171 warning - Windows Drivers ](https://docs.microsoft.com/en-us/windows-hardware/drivers/devtest/28171-function-has-more-than-one-page-macro-instance)\n\n## Semmle-specific notes\n**C++ function template support.** When the same source-level `PAGED_CODE` macro is expanded into multiple `FunctionTemplateInstantiation` bodies, the query collapses all instantiations of a single source-level template into one match key (via the underlying `TemplateFunction`) so duplicates inside a templated paged function body are reported once at the source-level location. Specialisations and non-template-paged functions are excluded.\n\n", + "markdown": "# Multiple instances of PAGED_CODE or PAGED_CODE_LOCKED\nThe function has more than one instance of PAGED_CODE or PAGED_CODE_LOCKED. This warning indicates that there is more than one instance of the PAGED_CODE or PAGED_CODE_LOCKED macro in a function. This error is reported at the second or subsequent instances of the PAGED_CODE or PAGED_CODE_LOCKED macro.\n\n\n## Recommendation\nRemove all but one PAGED_CODE OR PAGED_CODE_LOCKED macro.\n\n\n## Example\n\n```c\n// Copyright (c) Microsoft Corporation.\r\n// Licensed under the MIT license.\r\n\r\n\r\n//Macros to enable or disable a code section that may or maynot conflict with this test.\r\n#define SET_DISPATCH 1\r\n#define SET_PAGE_CODE 1\r\n\r\n\r\n_Dispatch_type_(IRP_MJ_CLEANUP) \r\nDRIVER_DISPATCH DispatchCleanup;\r\n\r\n_Dispatch_type_(IRP_MJ_SHUTDOWN)\r\nDRIVER_DISPATCH DispatchShutdown;\r\n\r\n#ifndef __cplusplus\r\n#pragma alloc_text (PAGE, DispatchCleanup)\r\n#pragma alloc_text (PAGE, DispatchShutdown)\r\n#endif\r\n\r\n\r\n//Template\r\nvoid top_level_call(){\r\n}\r\n\r\n//Passes\r\nNTSTATUS\r\nDispatchCleanup (\r\n PDEVICE_OBJECT DriverObject,\r\n PIRP Irp\r\n )\r\n{\r\n UNREFERENCED_PARAMETER(DriverObject);\r\n UNREFERENCED_PARAMETER(Irp);\r\n PAGED_CODE();\r\n \r\n return STATUS_SUCCESS;\r\n}\r\n\r\n//Fails\r\nNTSTATUS\r\nDispatchShutdown (\r\n PDEVICE_OBJECT DriverObject,\r\n PIRP Irp\r\n )\r\n{\r\n UNREFERENCED_PARAMETER(DriverObject);\r\n UNREFERENCED_PARAMETER(Irp);\r\n PAGED_CODE();\r\n PAGED_CODE();\r\n \r\n return STATUS_SUCCESS;\r\n}\r\n\r\n\r\n\r\n\r\n\r\n\n```\n\n## References\n* [ C28171 warning - Windows Drivers ](https://docs.microsoft.com/en-us/windows-hardware/drivers/devtest/28171-function-has-more-than-one-page-macro-instance)\n\n## Semmle-specific notes\n**C++ function template support.** When the same source-level `PAGED_CODE` macro is expanded into multiple `FunctionTemplateInstantiation` bodies, the query collapses all instantiations of a single source-level template into one match key (via the underlying `TemplateFunction`) so duplicates inside a templated paged function body are reported once at the source-level location. Specialisations and non-template-paged functions are excluded.\n\n" + }, + "properties": { + "tags": [ + "correctness", + "ca_ported", + "wddst" + ], + "description": "The function has more than one instance of PAGED_CODE or PAGED_CODE_LOCKED. This can cause issues when debugging, using Code Analysis, or running on checked builds.", + "feature.area": "Multiple", + "id": "cpp/drivers/multiple-paged-code", + "impact": "Insecure Coding Practice", + "kind": "problem", + "name": "Multiple instances of PAGED_CODE or PAGED_CODE_LOCKED", + "opaqueid": "CQLD-C28171", + "owner.email": "sdat@microsoft.com", + "platform": "Desktop", + "precision": "high", + "problem.severity": "warning", + "query-version": "v4", + "repro.text": "The following code locations are duplicate PAGED_CODE() calls within a function.", + "scope": "domainspecific", + "security.severity": "Low" + } + } + ] + }, + "extensions": [ + { + "name": "microsoft/windows-drivers", + "semanticVersion": "1.9.0+a76f8551b47f01adada99ddc44a5ea4fa9839fca", + "locations": [ + { + "uri": "file:///F:/source/repos/Windows-Driver-Developer-Supplemental-Tools/src/", + "description": { + "text": "The QL pack root directory." + }, + "properties": { + "tags": [ + "CodeQL/LocalPackRoot" + ] + } + }, + { + "uri": "file:///F:/source/repos/Windows-Driver-Developer-Supplemental-Tools/src/qlpack.yml", + "description": { + "text": "The QL pack definition file." + }, + "properties": { + "tags": [ + "CodeQL/LocalPackDefinitionFile" + ] + } + } + ] + }, + { + "name": "codeql/cpp-all", + "semanticVersion": "7.0.0+c5329f6f3863621c140ea7abd5954860e96c8bf1", + "locations": [ + { + "uri": "file:///C:/Users/natede/.codeql/packages/codeql/cpp-all/7.0.0/", + "description": { + "text": "The QL pack root directory." + }, + "properties": { + "tags": [ + "CodeQL/LocalPackRoot" + ] + } + }, + { + "uri": "file:///C:/Users/natede/.codeql/packages/codeql/cpp-all/7.0.0/qlpack.yml", + "description": { + "text": "The QL pack definition file." + }, + "properties": { + "tags": [ + "CodeQL/LocalPackDefinitionFile" + ] + } + } + ] + } + ] + }, + "invocations": [ + { + "toolExecutionNotifications": [ + { + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "driver/driver_snippet.c", + "uriBaseId": "%SRCROOT%", + "index": 0 + } + } + } + ], + "message": { + "text": "" + }, + "level": "none", + "descriptor": { + "id": "cpp/baseline/expected-extracted-files", + "index": 0 + }, + "properties": { + "formattedMessage": { + "text": "" + } + } + }, + { + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "driver/fail_driver1.h", + "uriBaseId": "%SRCROOT%", + "index": 1 + } + } + } + ], + "message": { + "text": "" + }, + "level": "none", + "descriptor": { + "id": "cpp/baseline/expected-extracted-files", + "index": 0 + }, + "properties": { + "formattedMessage": { + "text": "" + } + } + }, + { + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "driver/fail_driver1.c", + "uriBaseId": "%SRCROOT%", + "index": 2 + } + } + } + ], + "message": { + "text": "" + }, + "level": "none", + "descriptor": { + "id": "cpp/baseline/expected-extracted-files", + "index": 0 + }, + "properties": { + "formattedMessage": { + "text": "" + } + } + }, + { + "message": { + "text": "" + }, + "level": "none", + "timeUtc": "2026-04-29T04:41:39.691983100Z", + "descriptor": { + "id": "cli/file-coverage-baseline", + "index": 1 + }, + "properties": { + "attributes": { + "durationMilliseconds": 270 + }, + "visibility": { + "statusPage": false, + "telemetry": true + } + } + }, + { + "message": { + "text": "" + }, + "level": "none", + "timeUtc": "2026-04-29T04:41:39.700982700Z", + "descriptor": { + "id": "cli/platform", + "index": 2 + }, + "properties": { + "attributes": { + "arch": "amd64", + "name": "Windows 11", + "version": "10.0" + }, + "visibility": { + "statusPage": false, + "telemetry": true + } + } + }, + { + "message": { + "text": "Internal telemetry for the C++ extractor.\n\nNo action needed.", + "markdown": "Internal telemetry for the C++ extractor.\n\nNo action needed." + }, + "level": "note", + "timeUtc": "2026-04-29T04:42:21.983112500Z", + "descriptor": { + "id": "cpp/extractor/summary", + "index": 3 + }, + "properties": { + "attributes": { + "cache-hits": 0, + "cache-misses": 1, + "compilers": [ + { + "program": "cl", + "version": "Microsoft (R) C/C++ Optimizing Compiler Version 19.50.35724 for x64" + } + ], + "extractor-failures": 1, + "extractor-successes": 0, + "trap-caching": "disabled" + }, + "visibility": { + "statusPage": false, + "telemetry": true + } + } + } + ], + "executionSuccessful": true + } + ], + "artifacts": [ + { + "location": { + "uri": "driver/driver_snippet.c", + "uriBaseId": "%SRCROOT%", + "index": 0 + } + }, + { + "location": { + "uri": "driver/fail_driver1.h", + "uriBaseId": "%SRCROOT%", + "index": 1 + } + }, + { + "location": { + "uri": "driver/fail_driver1.c", + "uriBaseId": "%SRCROOT%", + "index": 2 + } + } + ], + "results": [ + { + "ruleId": "cpp/drivers/multiple-paged-code", + "ruleIndex": 0, + "rule": { + "id": "cpp/drivers/multiple-paged-code", + "index": 0 + }, + "message": { + "text": "Functions in a paged section must have exactly one instance of the PAGED_CODE or PAGED_CODE_LOCKED macro" + }, + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "driver/driver_snippet.c", + "uriBaseId": "%SRCROOT%", + "index": 0 + }, + "region": { + "startLine": 50, + "startColumn": 5, + "endColumn": 17 + } + } + } + ], + "partialFingerprints": { + "primaryLocationLineHash": "c7556935fb8cd898:1", + "primaryLocationStartColumnFingerprint": "0" + } + } + ], + "columnKind": "utf16CodeUnits", + "properties": { + "semmle.formatSpecifier": "sarifv2.1.0" + } + } + ] +} diff --git a/src/drivers/wdm/queries/MultiplePagedCode/driver_snippet.c b/src/drivers/wdm/queries/MultiplePagedCode/driver_snippet.c index 4bc016a1..7af85a9f 100644 --- a/src/drivers/wdm/queries/MultiplePagedCode/driver_snippet.c +++ b/src/drivers/wdm/queries/MultiplePagedCode/driver_snippet.c @@ -2,7 +2,7 @@ // Licensed under the MIT license. -//Macros to enable or disable a code section that may or maynot conflict with this test. +// Macros to enable or disable a code section that may or may not conflict with this test. #define SET_DISPATCH 1 #define SET_PAGE_CODE 1