Skip to content

Anatomy of a Secure Driver

Six anti-patterns behind most Windows kernel driver vulnerabilities -- and how to avoid them.

Root Cause Distribution

Across 134 CVEs in the KernelSight corpus, the majority stem from a small set of root causes. Unvalidated input dominates -- missing length checks, unchecked file offsets, unbounded copies. The remaining categories occur less frequently but share a common theme: the driver trusts something it shouldn't.

FIG — Root Cause Distribution (134 CVEs) Unvalidated Input ~60% Improper Sync ~14% Other ~10% Type / Lifetime ~6% Authorization Gaps ~4% Error-Path Bypass ~4% Double-Fetch ~2%

Categories mapped from 134 CVEs in the KernelSight corpus. "Unvalidated Input" includes missing length checks, unchecked offsets, and unbounded copies.

The Six Anti-Patterns

1. Trusting User-Supplied Lengths

What goes wrong -- The driver reads a length or size from userland and passes it straight to a copy or allocation without checking it against the actual buffer size. CVE-2025-5942 (epdlpdrv.sys heap overflow), CVE-2024-38054 (ks.sys property request overflow), CVE-2025-24993 (ntfs.sys heap overflow from crafted VHD), and CVE-2025-24985 (FAT integer overflow in fastfat.sys) all stem from this.

The fix -- Validate InputBufferLength before touching the buffer. Cap copy sizes against the allocation.

if (InputBufferLength < sizeof(MY_HEADER))
    return STATUS_BUFFER_TOO_SMALL;

if (header->DataSize > InputBufferLength - sizeof(MY_HEADER))
    return STATUS_INVALID_PARAMETER;

RtlCopyMemory(dst, src, header->DataSize);

2. Missing Synchronization on Shared State

What goes wrong -- Concurrent threads access shared driver state -- a reference count, a linked list, a pointer field -- without adequate locking. The window is usually narrow but sufficient for exploitation. CVE-2026-21241 (afd.sys notification UAF) hits a race between registration and teardown. CVE-2024-38106 (ntoskrnl token race), CVE-2025-32701 (clfs.sys UAF), and CVE-2023-36802 (mskssrv.sys pipe UAF) follow the same pattern.

The fix -- Use reference counting for objects accessed from multiple contexts. Protect mutable state with spin locks or push locks. Establish a lock ordering convention and stick to it.

// Reference-counted teardown
if (InterlockedDecrement(&Object->RefCount) == 0) {
    ExFreePoolWithTag(Object, TAG);
}

3. Trusting On-Disk / File-Embedded Offsets

What goes wrong -- File system and log drivers read offset/index fields from on-disk structures and use them as memory indices without bounds checking. A crafted image triggers out-of-bounds access. CLFS contains three such CVEs: CVE-2025-29824, CVE-2022-37969, and CVE-2023-28252 -- all exploited in the wild. CVE-2025-24992 (ntfs.sys) is another example.

The fix -- Treat on-disk data with the same suspicion as user input. Bounds-check every offset and index against the container or record size before use.

if (record->FieldOffset + record->FieldLength > ContainerSize)
    return STATUS_LOG_CORRUPTION;

4. Exposing Physical Memory or Arbitrary MSR Access

What goes wrong -- The driver maps physical memory to userland or exposes model-specific register (MSR) read/write via IOCTLs. This gives any caller full kernel read/write by design. CVE-2021-21551 (Dell DBUtil), CVE-2019-16098 (MSI RTCore64), CVE-2020-12928 (AMD Ryzen Master), and Capcom.sys are well-known examples. All are used in BYOVD campaigns.

The fix -- Don't ship physical memory mapping or MSR access in production drivers. If hardware diagnostics require it, gate behind a restrictive DACL, limit to specific physical ranges, and never expose it in a signed driver intended for end-user systems.

5. No IOCTL Authentication / Open Device ACLs

What goes wrong -- The driver creates its device object with a permissive ACL, allowing any user to open the device and send IOCTLs. Some drivers don't check caller identity at all. CVE-2025-3464 (AsIO3.sys auth bypass), CVE-2023-1048 (KProcessHacker unrestricted IOCTLs), CVE-2025-0289 (Paragon BioNTdrv.sys), and CVE-2025-68947 (NSecKrnl) all allow unprivileged callers to reach dangerous code paths.

The fix -- Set a restrictive DACL at IoCreateDeviceSecure or via an INF security descriptor. Enforce per-IOCTL access checks for sensitive operations.

// Restrictive SDDL: SYSTEM + Administrators only
DECLARE_CONST_UNICODE_STRING(sddl,
    L"D:P(A;;GA;;;SY)(A;;GA;;;BA)");
IoCreateDeviceSecure(..., &sddl, ...);

6. Double-Fetch / TOCTOU on User Buffers

What goes wrong -- The driver reads a value from a user-mode buffer, validates it, then reads the same location again. An attacker flips the value between the two reads. CVE-2024-11616 (epdlpdrv.sys double-fetch of length field), CVE-2024-30088 (ntoskrnl handle-to-pointer race), and CVE-2024-38106 (ntoskrnl token assignment race) all exploit this gap.

The fix -- Capture user data into a kernel buffer in a single copy. Validate the kernel-side copy. Never re-read from userland.

// Capture once, validate the copy
MY_REQUEST req;
__try {
    ProbeForRead(UserBuffer, sizeof(req), __alignof(MY_REQUEST));
    req = *UserBuffer;  // single capture
} __except (EXCEPTION_EXECUTE_HANDLER) {
    return GetExceptionCode();
}
// All validation uses 'req', never *UserBuffer

A Note on BYOVD

Even a vulnerability-free driver becomes a weapon if it exposes raw kernel read/write by design. Drivers that map physical memory, disable code integrity, or provide arbitrary MSR access are routinely dropped by ransomware groups and APTs -- no exploit needed. See BYOVD for the full pattern.

Secure Driver Checklist

# Check Anti-Pattern Blocked Example CVE
1 Validate InputBufferLength before any buffer access Trusting user-supplied lengths CVE-2025-5942
2 Cap copy size against allocation size Trusting user-supplied lengths CVE-2024-38054
3 Check for integer overflow in size arithmetic Trusting user-supplied lengths CVE-2025-24985
4 Reference-count shared objects Missing synchronization CVE-2026-21241
5 Hold locks across check-and-use sequences Missing synchronization CVE-2024-38106
6 Bounds-check all on-disk offsets against container size Trusting on-disk offsets CVE-2025-29824
7 Validate on-disk index values before array access Trusting on-disk offsets CVE-2022-37969
8 Never map physical memory to userland Exposing physical memory CVE-2021-21551
9 Never expose MSR read/write via IOCTL Exposing physical memory CVE-2019-16098
10 Set restrictive DACL on device object Open device ACLs CVE-2025-3464
11 Enforce per-IOCTL access checks Open device ACLs CVE-2023-1048
12 Capture user buffers into kernel memory once Double-fetch / TOCTOU CVE-2024-11616
13 Validate the kernel-side copy, never re-read userland Double-fetch / TOCTOU CVE-2024-30088
14 Use METHOD_BUFFERED or probe-and-capture for direct I/O Double-fetch / TOCTOU CVE-2024-38106

Detection & Audit Pointers

Static analysis catches most input validation and double-fetch bugs at scale -- see Static Analysis. Fuzzing covers synchronization and error-path categories where static tools struggle. AutoPiff automates diff-based detection across Patch Tuesday drops.

Cross-References