Skip to content

Reduce false positives compared to SDV for driver tests#217

Merged
NateD-MSFT merged 9 commits intomicrosoft:developmentfrom
zx2c4:jd/false-positives
Apr 23, 2026
Merged

Reduce false positives compared to SDV for driver tests#217
NateD-MSFT merged 9 commits intomicrosoft:developmentfrom
zx2c4:jd/false-positives

Conversation

@zx2c4
Copy link
Copy Markdown

@zx2c4 zx2c4 commented Apr 13, 2026

Moving from SDV to CodeQL introduced a lot of false positives that I've had to suppress. This small series mostly gets things back on par with SDV for my project WireGuardNT. There's extensive description in each commit message, so be sure to review this commit-by-commit.

zx2c4 added 2 commits April 13, 2026 01:45
The double-fetch query flags pairs of dereferences through memory
origins where originCanWrite() is true. MdlOrigin had this set
unconditionally, so every pair of accesses through an MDL-mapped pointer
was flagged, even pure writes to an output buffer:

  void *Buf = MmGetSystemAddressForMdlSafe(Mdl, NormalPagePriority);
  ((MY_IOCTL *)Buf)->Flags = 0;     // flagged
  ((MY_IOCTL *)Buf)->Count = 0;     // flagged

While usermode does retain write access to MDL-locked pages through its
original VA, flagging all of these is not useful in practice, since
virtually all direct I/O drivers access the buffer more than once.

Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
The query only allowed MajorFunction access directly inside a
DriverEntry function. Drivers that split initialization across helper
functions were falsely flagged:

  VOID InitDispatch(DRIVER_OBJECT *DriverObject) {
      DriverObject->MajorFunction[IRP_MJ_CREATE] = MyCreate;
  }
  NTSTATUS DriverEntry(DRIVER_OBJECT *DriverObject, ...) {
      InitDispatch(DriverObject);
  }

Follow call chains transitively from DriverEntry so that helpers are
recognized.

Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
@zx2c4
Copy link
Copy Markdown
Author

zx2c4 commented Apr 13, 2026

CC @NateD-MSFT

@NateD-MSFT
Copy link
Copy Markdown
Collaborator

@zx2c4 Acknowledging. Will review when I have free cycles.

@zx2c4
Copy link
Copy Markdown
Author

zx2c4 commented Apr 13, 2026

Thanks! I think you have to at least click some button to let the CI work? Not sure. Anyway, will sit tight.

@zx2c4
Copy link
Copy Markdown
Author

zx2c4 commented Apr 13, 2026

Those test failures seem more to do with the testing infra?

@NateD-MSFT
Copy link
Copy Markdown
Collaborator

Those test failures seem more to do with the testing infra?

Yes, I should have proactively mentioned that - we have various problems with our pipeline at the moment (tracked in #197). The core build section of the pipeline looks to have passed, however.

@zx2c4
Copy link
Copy Markdown
Author

zx2c4 commented Apr 13, 2026

Ah okay. I'll leave this be, then. Hopefully you can get to it before the next HLK release!

@NateD-MSFT
Copy link
Copy Markdown
Collaborator

So far this LGTM but I'm running it on our internal test corpuses to check the diff there before approving. The contribution is much appreciated.

Copy link
Copy Markdown
Collaborator

@NateD-MSFT NateD-MSFT left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Largely looks good to me. See my question about where to put the When logic for analyzing function calls in IRQL queries.

Comment thread src/drivers/general/queries/IrqlTooHigh/IrqlTooHigh.ql Outdated
Comment thread src/drivers/general/queries/IrqlTooHigh/IrqlTooHigh.ql Outdated
Comment thread src/drivers/general/queries/IrqlTooHigh/IrqlTooHigh.ql Outdated
Comment thread src/drivers/general/queries/IrqlTooHigh/IrqlTooHigh.ql Outdated
zx2c4 added 6 commits April 21, 2026 15:16
KeSetEvent is annotated _When_(Wait==1, _IRQL_requires_max_(APC_LEVEL)).
When called with Wait=FALSE, the restriction does not apply and
DISPATCH_LEVEL is fine. The query unconditionally applied the annotation
regardless of argument values:

  KeAcquireInStackQueuedSpinLock(&Lock, &Handle); // raises to DISPATCH
  KeSetEvent(&Event, IO_NO_INCREMENT, FALSE);     // flagged

Evaluate _When_ conditions against compile-time argument values and skip
annotations whose conditions are demonstrably false.

Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
getIrqlLevel() returns -1 when it cannot parse the annotation, such as
_IRQL_saves_ or _When_ conditionals with complex expressions. The query
flagged all of these as invalid annotations:

  _IRQL_saves_
  VOID KeAcquireSpinLock(PKSPIN_LOCK Lock, PKIRQL OldIrql); // flagged

Filter out the -1 sentinel since these are valid annotations beyond the
analyzer's current parsing capability.

Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
DRIVER_DISPATCH_PAGED is a valid paged variant of DRIVER_DISPATCH for
dispatch routines that only run at PASSIVE_LEVEL. The query flagged the
mismatch between function class and typedef:

  _Dispatch_type_(IRP_MJ_DEVICE_CONTROL)
  static DRIVER_DISPATCH_PAGED MyDispatch; // flagged

Recognize _PAGED suffixed typedefs as compatible with their unsuffixed
base function class.

Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
Drivers sometimes construct synthetic MDLs on the stack for zero-copy
operations. Direct field access is the only way to initialize these:

  MDL Mdl = { 0 };
  Mdl.MappedSystemVa = Buffer;
  Mdl.ByteCount = Len;
  Mdl.MdlFlags = MDL_MAPPED_TO_SYSTEM_VA;

Exclude accesses on locally-declared MDL struct variables. Accesses
through MDL pointers are not excluded, since those typically reference
system-provided MDLs.

Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
When a possibly-null return value is passed to a function whose
parameter is annotated _In_opt_, the function explicitly handles null.
The query was flagging these as unguarded dereferences:

  OBJ *Obj = LookupObject(...);
  PutObject(Obj);  // flagged, but PutObject takes _In_opt_ OBJ *

Exclude calls where the argument's corresponding parameter carries an
_opt_ SAL annotation.

Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
_Analysis_assume_(Expr) tells the analyzer to treat Expr as true. MSVC
compiles __assume() into an empty statement with no AST node, but the
EmptyStmt remains in the control flow graph at the macro invocation
site. The query was not recognizing this as a null guard:

  NBL *Nbl = DequeueNbl(&Queue);
  _Analysis_assume_(Nbl);
  NET_BUFFER_LIST_STATUS(Nbl) = NDIS_STATUS_FAILURE;  // flagged

Match the EmptyStmt at the _Analysis_assume_ location as a barrier by
correlating it with the macro invocation that names the guarded
variable. Also match AssumeExpr directly for compilers that emit it.

Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
@zx2c4 zx2c4 force-pushed the jd/false-positives branch from c7db942 to 0576474 Compare April 21, 2026 13:17
@zx2c4
Copy link
Copy Markdown
Author

zx2c4 commented Apr 21, 2026

Alright, fixed up your two remarks. Hopefully good to go now.

zx2c4-bot pushed a commit to WireGuard/wireguard-nt that referenced this pull request Apr 21, 2026
I fixed the CodeQL rules.

Link: microsoft/Windows-Driver-Developer-Supplemental-Tools#217
Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
@zx2c4
Copy link
Copy Markdown
Author

zx2c4 commented Apr 22, 2026

@NateD-MSFT I think you have to press the buttons for the CI to run again. But anyway, hopefully this looks good to you now.

@NateD-MSFT
Copy link
Copy Markdown
Collaborator

@zx2c4 Actually, I'm finishing up a PR to get our CI up and running again (#218), then I'll move this PR to target our development branch, let the CI run, and merge it after that. I just finished running against our internal corpus again and things look good.

Thanks again for the hard work.

@NateD-MSFT NateD-MSFT changed the base branch from main to development April 23, 2026 00:46
@NateD-MSFT
Copy link
Copy Markdown
Collaborator

The only failure is on our end for a misconfiguration (publish shouldn't be running on PR, just post-PR commits.) Merging.

@NateD-MSFT NateD-MSFT merged commit 219f97f into microsoft:development Apr 23, 2026
6 of 7 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants