# ntoskrnl.exe Patch Diff — CVE-2026-42980

| | |
|---|---|
| Binary | ntoskrnl.exe (Windows NT OS Kernel) |
| Pre-patch version | 10.0.26100.8737 (Windows 11 24H2, KB5095093, June 2026) |
| Post-patch version | 10.0.26100.8875 (Windows 11 24H2, KB5101650, July 2026) |
| KB | KB5101650 |
| CVE | CVE-2026-42980 — Windows NT OS Kernel Elevation of Privilege (WMI integer underflow → OOB write), CVSS 7.8 / temporal 6.8, MSRC severity Important, CWE-191 + CWE-122 |
| Diff tool | ghidriff (Ghidra VersionTrackingDiff, Ghidra 12.1.2, full ntkrnlmp.pdb symbols), cross-checked live via GhidraMCP |
| Vulnerable functions | `nt!WmipQueryAllDataMultiple`, `nt!WmipQuerySingleMultiple` |
| Fix pattern | **CFR flag enablement** — the saturating-subtraction code ships in BOTH builds behind WIL flag `Feature_1045423416`; KB5101650 flips the flag on |

> **Status: VERIFIED.** ghidriff diff completed (70,713 / 70,714 functions
> matched; only 6 code-changed, none of them in the WMI path) and the finding
> below was confirmed by live decompilation of the pre-patch binary.

## The headline: a flag-flip patch

The ghidriff function-level diff between .8737 and .8875 shows **no code
change in `WmipQuerySingleMultiple` or `WmipQueryAllDataMultiple`** — and that
is the answer, not a tooling failure. Live decompilation of the **pre-patch**
.8737 binary (`WmipQuerySingleMultiple` @ `0x1407a4fc0`,
`WmipQueryAllDataMultiple` in the same build) shows the fix was already
compiled into both functions, sitting dormant behind the WIL
Controlled-Feature-Rollout flag `Feature_1045423416`:

```c
// nt!WmipQuerySingleMultiple @ 0x1407a4fc0 (PRE-PATCH .8737 — verified via GhidraMCP)
uVar12 = (uStack_334 + 7) & 0xfffffff8;          // consumed, aligned up
uVar14 = uStack_330 - uVar12;                    // remaining -= consumed  (can underflow)
...
iVar6 = Feature_1045423416__private_IsEnabledDeviceUsageNoInline();
if (iVar6 != 0) {
    uVar14 = -(uint)(uVar12 < uStack_330) & uVar14;   // saturate to 0 on underflow
}
uStack_330 = uVar14;

// nt!WmipQueryAllDataMultiple (PRE-PATCH .8737 — verified via GhidraMCP)
uVar9 = (iStack_104 + 7) & 0xfffffff8;
iVar4 = Feature_1045423416__private_IsEnabledDeviceUsageNoInline();
if (iVar4 == 0) {
    param_6 = param_6 - uVar9;                              // vulnerable path
} else {
    param_6 = -(uint)(uVar9 < param_6) & (param_6 - uVar9); // saturating path
}
```

Both `Feature_1045423416__private_IsEnabledDeviceUsageNoInline` @ `0x14064a9d4`
and `Feature_1045423416__private_IsEnabledFallback` @ `0x14064aa0c` already
exist in the pre-patch build. What KB5101650 changed is the **flag's state**
(staged/rollout data and servicing metadata), turning the safe branch on.
This is Microsoft's standard CFR kill-switch pattern: ship the fixed code
disabled, enable it via the monthly update.

## Vulnerability mechanics (both builds, flag OFF)

Both WMI "multiple" serialization paths maintain a 32-bit *remaining output*
counter while walking WNODE items. With the flag off they subtract an
**aligned, provider-influenced size** without proving it fits:

```c
// nt!WmipQueryAllDataMultiple  (IOCTL 0x22812C) — sub @ +0x29a
AlignedSize = (QueryInfo[0] + 7) & 0xFFFFFFF8;
CurrentOutputBuffer += AlignedSize;
TotalSize += AlignedSize;
OutputBufferLength -= AlignedSize;          // vulnerable: unchecked

// nt!WmipQuerySingleMultiple   (IOCTL 0x228130) — sub @ +0x401
AlignedActualSize = (ReturnedDataSize + 7) & 0xFFFFFFF8;
TotalRequiredSize += AlignedActualSize;
OutBufferSize -= AlignedActualSize;         // vulnerable: unchecked
```

If `Aligned(Actual)Size > Remaining`, the unsigned counter wraps to a huge
value (e.g. `0x94 - 0x98 = 0xFFFFFFFC`), and the next WNODE item is
serialized **past the end of the kernel SystemBuffer** — a controlled
out-of-bounds write into adjacent pool memory.

With the flag on, the mask `-(uint)(aligned < remaining)` is `0xFFFFFFFF`
only when the subtraction does not underflow; on underflow the remaining
counter clamps to `0`, the next item fails the capacity gate, and no OOB
write occurs.

## ghidriff diff results (what actually changed in the binary)

Full VersionTracking diff, ntoskrnl 10.0.26100.8737 vs 10.0.26100.8875:

| Metric | Value |
|---|---|
| Functions matched | 70,713 / 70,714 |
| Functions with code changes | 6 |
| Functions added | 1 (`Feature_3244801339__private_IsEnabledFallback`) |
| Changes in WMI path | **0 — code identical; fix is the flag state** |

The 6 code-changed functions are unrelated servicing churn:

| Function | Area |
|---|---|
| `RtlpCheckFunctionPatchAppliedInOriginalImage` | hotpatch bookkeeping |
| `FsRtlpRequestShareableOplock` / `FsRtlpComputeShareableOplockState` | FSRTL oplock logic |
| `SepSecureBootCorrectBcd` / `NtFilterBootOption` | Secure Boot / boot options |
| `RtlpHpVsSlotCreate` | heap vs-subsegment |

None of these touch the WMI query path — consistent with a CFR enablement
rather than a code rebuild of the fix.

### Why the classic diff "missed" it

`Feature_1045423416` is a WIL (`wil`) feature flag. Its enabled state is not
encoded in the function bodies; it lives in servicing/rollout data that
ghidriff's function matcher does not compare. Any patch-diff of a CFR-flagged
fix must therefore also enumerate **new `Feature_*` symbols and flag-state
data**, or decompile the suspect functions in BOTH builds to confirm whether
the "patched" code already existed pre-patch.

## Userspace reach (hint)

Reachable from any user process via the WMI device interface:

- Open the WMI device `\\.\WMIDataDevice`
- `DeviceIoControl` with `IOCTL_WMI_QUERY_ALL_DATA_MULTIPLE` (**0x22812C**) or
  `IOCTL_WMI_QUERY_SINGLE_MULTIPLE` (**0x228130**) over multiple WMI GUIDs,
  where one provider returns a `DataSize` whose 8-byte alignment exceeds the
  remaining buffer by a few bytes (the 4-byte "slop" case: caller estimate
  `(DataSize + 73) & ~7` passes the gate, provider's `ALIGN8(ReturnedDataSize)`
  underflows the counter).

See `poc/poc_cve_2026_42980.c` for a full trigger skeleton and
`poc/poc_cve_2026_42980_wmi_guids.h` for the GUID set.

---

<sub>Source: ghidriff diff of ntoskrnl-10.0.26100.8737.exe (KB5095093,
pre-patch) vs ntoskrnl-10.0.26100.8875.exe (KB5101650, post-patch), Windows 11
24H2 — **completed**; raw report in
`ghidriff/CVE-2026-42980/ghidriffs/`. Flag-flip verified by live GhidraMCP
decompilation of the pre-patch binary (`WmipQuerySingleMultiple` @
0x1407a4fc0, `Feature_1045423416__private_IsEnabledDeviceUsageNoInline` @
0x14064a9d4). Mechanism and userspace offsets per the G4sp4rCS public
analysis ([writeup](https://github.com/G4sp4rCS/CVE-2026-42980-POC/blob/main/writeup-en.md)).</sub>
