Patch Patterns
What Microsoft's kernel fixes look like for each bug class -- with before/after pseudocode and AutoPiff rule mapping.
Why Patches Are Predictable
Kernel patches tend to be surgical. A typical Patch Tuesday fix adds 5-20 lines of validation code without restructuring the surrounding function. This conservatism is intentional: Microsoft needs to fix the vulnerability without introducing regressions or changing driver behavior for legitimate callers. The result is that patches follow a small number of recognizable shapes, each corresponding to a specific vulnerability class.
Recognizing these shapes is a practical skill. When you diff a binary update, the patch pattern tells you what the vulnerability was, often before the CVE details are public. When you audit a driver, knowing the fix shapes tells you what to look for in the original code. And when you build automated analysis (like AutoPiff), the patterns become detection rules that can classify patches at scale.
This page catalogs the 7 most common patch shapes across the KernelSight corpus. Each pattern maps to AutoPiff detection rules and references the CVEs where Microsoft applied that specific fix.
Patch Categories
1. Added Bounds Check
Pattern: A new if (len > max) return STATUS_... guard appears before a copy, allocation, or pointer arithmetic operation.
Where it appears: Buffer overflow and out-of-bounds access fixes. This is the most common patch type in the corpus, reflecting the dominance of unvalidated input as a root cause (see Secure Driver Anatomy, anti-pattern 1).
Before:
// No validation -- copies whatever length the caller provides
RtlCopyMemory(dst, src, header->DataSize);
After:
if (header->DataSize > AllocationSize - FIELD_OFFSET(MY_STRUCT, Data))
return STATUS_INVALID_PARAMETER;
RtlCopyMemory(dst, src, header->DataSize);
CVE examples:
- CVE-2022-37969 -- CLFS cbSymbolZone bounds check added
- CVE-2024-49138 -- CLFS LoadContainerQ length validation
- CVE-2025-24993 -- ntfs.sys MFT attribute size check
- CVE-2025-21418 -- afd.sys buffer length validation
2. Added Lock / Synchronization
Pattern: A spin lock, push lock, or InterlockedCompareExchange guard wraps a previously unprotected read-modify-write sequence on shared state.
Where it appears: Race condition and some UAF fixes. The second most common patch type, reflecting the prevalence of synchronization bugs in multi-threaded kernel code.
Before:
// No lock -- another thread can free Object between check and use
if (Object->State == ACTIVE) {
ProcessObject(Object);
}
After:
KeAcquireSpinLock(&Object->Lock, &OldIrql);
if (Object->State == ACTIVE) {
ProcessObject(Object);
}
KeReleaseSpinLock(&Object->Lock, OldIrql);
CVE examples:
- CVE-2024-38106 -- ntoskrnl lock added around VslpEnterIumSecureMode
- CVE-2026-21241 -- afd.sys spinlock scope extended to cover notification teardown
- CVE-2025-62215 -- ntoskrnl synchronization for double-free prevention
3. Added Probe / Capture
Pattern: ProbeForRead/ProbeForWrite followed by a single-copy capture replaces direct dereference of a user-mode pointer. The kernel validates and captures user data once instead of reading it multiple times.
Where it appears: Double-fetch and TOCTOU fixes. This pattern eliminates the race window by ensuring the kernel only reads from user memory once, then operates on its own copy.
Before:
// Reads user buffer twice -- attacker can change value between reads
if (UserBuffer->Length <= MAX_LEN) {
RtlCopyMemory(dst, UserBuffer->Data, UserBuffer->Length); // re-read
}
After:
__try {
ProbeForRead(UserBuffer, sizeof(MY_REQUEST), __alignof(MY_REQUEST));
CapturedLength = UserBuffer->Length; // single capture
} __except (EXCEPTION_EXECUTE_HANDLER) {
return GetExceptionCode();
}
if (CapturedLength <= MAX_LEN) {
RtlCopyMemory(dst, src, CapturedLength); // uses captured value
}
CVE examples:
- CVE-2024-30088 -- ntoskrnl probe-and-capture for security attributes
- CVE-2024-11616 -- epdlpdrv.sys single-copy capture replacing double-fetch
4. Added IOCTL Access Control
Pattern: The patch adds caller identity checks to an IOCTL dispatcher: SeSinglePrivilegeCheck, PsGetCurrentProcessSessionId, or replaces IoCreateDevice with IoCreateDeviceSecure and a restrictive SDDL string.
Where it appears: Authorization bypass fixes. Common in inbox drivers where the original developer did not anticipate adversarial callers, and dominant in BYOVD driver patches where the fix is restricting who can reach the dangerous functionality.
Before:
// Any process can open the device and send this IOCTL
case IOCTL_DANGEROUS_OPERATION:
DoTheDangerousThing(Irp);
break;
After:
case IOCTL_DANGEROUS_OPERATION:
if (!SeSinglePrivilegeCheck(SeLoadDriverPrivilege,
Irp->RequestorMode)) {
status = STATUS_ACCESS_DENIED;
break;
}
DoTheDangerousThing(Irp);
break;
CVE examples:
- CVE-2024-21338 -- appid.sys IOCTL 0x22A018 access check added
- CVE-2024-26229 -- csc.sys missing access check patched
- CVE-2025-3464 -- AsIO3.sys hardlink auth bypass fix
5. Added Reference Counting
Pattern: InterlockedIncrement/InterlockedDecrement pairs appear around object acquisition and release paths. The object is only freed when the reference count reaches zero, preventing premature deallocation.
Where it appears: UAF fixes where the root cause is freeing an object while other code paths still hold stale pointers to it.
Before:
// Free without checking if anyone still holds a reference
RemoveFromList(Object);
ExFreePoolWithTag(Object, TAG);
After:
RemoveFromList(Object);
if (InterlockedDecrement(&Object->RefCount) == 0) {
ExFreePoolWithTag(Object, TAG);
}
CVE examples:
- CVE-2024-30089 -- mskssrv.sys ref-count logic error fix
- CVE-2024-38193 -- afd.sys Registered I/O buffer lifetime management
- CVE-2025-32709 -- afd.sys socket closure ref-count fix
6. Removed Dangerous Functionality
Pattern: An entire IOCTL handler, code path, or exported function is deleted. The patch does not fix the vulnerability; it removes the attack surface entirely.
Where it appears: BYOVD drivers after disclosure. Microsoft's Vulnerable Driver Blocklist entries also fall into this category, preventing the driver from loading at all. This is the bluntest patch pattern, and the only correct response when the functionality is dangerous by design. There is no safe way to expose physical memory mapping to userland.
CVE examples:
- AsIO3.sys -- blocklisted after CVE-2025-3464 / CVE-2025-1533
- ThrottleStop.sys -- CVE-2025-7771, MSR write removed, driver blocklisted
- BioNTdrv.sys -- CVE-2025-0289, five CVEs led to driver blocklist entry
7. Added Type / Object Validation
Pattern: A type check, object signature validation, or runtime tag comparison appears before a pointer cast or object dereference. The patch rejects objects that do not match the expected type.
Where it appears: Type confusion fixes, primarily in win32k and Kernel Streaming drivers where objects from different categories share similar interfaces.
Before:
// Cast without checking -- wrong object type causes corruption
PWINDOW_OBJECT Window = (PWINDOW_OBJECT)Object;
Window->ExtraData = NewValue;
After:
if (Object->Type != OBJECT_TYPE_WINDOW) {
return STATUS_OBJECT_TYPE_MISMATCH;
}
PWINDOW_OBJECT Window = (PWINDOW_OBJECT)Object;
Window->ExtraData = NewValue;
CVE examples:
- CVE-2022-21882 -- win32kbase.sys ConsoleWindow flag type check
- CVE-2023-36802 -- mskssrv.sys FsContextReg/FsStreamReg validation
AutoPiff Rule Mapping
AutoPiff detects these patch patterns automatically during binary diff analysis. Each pattern maps to one or more detection rules that fire when the corresponding code change is identified in a function diff.
| Patch Pattern | AutoPiff Rule(s) | Example CVE |
|---|---|---|
| Added Bounds Check | added_length_check, added_offset_bounds_check |
CVE-2024-49138 |
| Added Lock / Sync | added_spinlock_acquire, added_interlocked_op |
CVE-2024-38106 |
| Added Probe / Capture | added_probe_for_read, added_user_capture |
CVE-2024-30088 |
| Added IOCTL Access Control | added_access_check, modified_device_create |
CVE-2024-21338 |
| Added Reference Counting | added_ref_count, modified_object_free |
CVE-2024-30089 |
| Removed Functionality | removed_ioctl_handler, deleted_code_path |
CVE-2025-3464 |
| Added Type Validation | added_type_check, added_object_tag_check |
CVE-2022-21882 |
See AutoPiff Integration for setup and rule configuration.
Cross-References
- Corpus Analytics -- data distribution behind these patterns
- Exploit Chain Patterns -- the chains these patches break
- Anatomy of a Secure Driver -- the anti-patterns these patches fix
- Patch Diffing -- tools for identifying these patterns in binaries
- AutoPiff Integration -- automated detection at scale