Kernel-Mode Driver Framework (KMDF), the Microsoft-recommended way to write Windows kernel-mode drivers. Covers DriverEntry, EvtDeviceAdd, IRPs and IOCTLs, I/O queues, PnP and Power state machines, IRQL discipline, memory pools, WPP tracing, SAL annotations, and Driver Verifier. USE WHEN: user mentions "KMDF", "WDF kernel", "Windows kernel driver", "DriverEntry", "WdfDriverCreate", "EvtDeviceAdd", "IRP", "IOCTL", "DISPATCH_LEVEL", "PASSIVE_LEVEL", "NTSTATUS", "PoolTag", "WdfRequestComplete" DO NOT USE FOR: UMDF v2 (use `wdf-umdf`), classic WDM-only drivers, file-system filters (FltMgr is a separate framework)

GitHub
Install command
npx skhub add claude-dev-suite/wdf-kmdf
Markdown
SKILL.md

KMDF - Quick Reference

Deep Knowledge: Use mcp__documentation__fetch_docs with technology: wdf-kmdf. Authoritative source: learn.microsoft.com/en-us/windows-hardware/drivers/wdf/.

Driver entry + device add

NTSTATUS DriverEntry(_In_ PDRIVER_OBJECT DriverObject,
                     _In_ PUNICODE_STRING RegistryPath) {
    WDF_DRIVER_CONFIG config;
    WDF_DRIVER_CONFIG_INIT(&config, MyEvtDeviceAdd);
    config.DriverPoolTag = 'rvDM';
    return WdfDriverCreate(DriverObject, RegistryPath,
                           WDF_NO_OBJECT_ATTRIBUTES, &config, WDF_NO_HANDLE);
}

NTSTATUS MyEvtDeviceAdd(_In_ WDFDRIVER Driver, _Inout_ PWDFDEVICE_INIT DeviceInit) {
    UNREFERENCED_PARAMETER(Driver);

    // Filter? Function? FDO/PDO?
    // WdfFdoInitSetFilter(DeviceInit);   // <-- uncomment for filter
    WDFDEVICE device;
    NTSTATUS status = WdfDeviceCreate(&DeviceInit, WDF_NO_OBJECT_ATTRIBUTES, &device);
    if (!NT_SUCCESS(status)) return status;

    WDF_IO_QUEUE_CONFIG q;
    WDF_IO_QUEUE_CONFIG_INIT_DEFAULT_QUEUE(&q, WdfIoQueueDispatchParallel);
    q.EvtIoDeviceControl  = MyEvtIoDeviceControl;
    q.EvtIoRead           = MyEvtIoRead;
    q.EvtIoWrite          = MyEvtIoWrite;
    return WdfIoQueueCreate(device, &q, WDF_NO_OBJECT_ATTRIBUTES, NULL);
}

I/O queues — dispatch types

TypeBehavior
WdfIoQueueDispatchSequentialOne request at a time
WdfIoQueueDispatchParallelMany concurrent requests (your callbacks must be reentrant)
WdfIoQueueDispatchManualYou retrieve requests with WdfIoQueueRetrieveNextRequest

Use manual queues for forwarding patterns (filter holds requests, completes them later when the underlying device responds).

IRQL Cheat Sheet

IRQLAllowedForbidden
PASSIVE_LEVEL (0)Anything: paging OK, blocking OK—
APC_LEVEL (1)Most things; APCs disabled—
DISPATCH_LEVEL (2)Spinlocks, nonpaged pool, DPCsPage faults, blocking, paged pool, KeWaitForSingleObject (with timeout > 0)
DIRQL / HIGH_LEVELISR work onlyAlmost everything else

Annotate every function so SDV / CodeQL can verify:

_IRQL_requires_max_(DISPATCH_LEVEL)
_Must_inspect_result_
NTSTATUS Helper(_In_ WDFDEVICE dev);

Memory allocation

// Modern (preferred)
PVOID p = ExAllocatePool2(POOL_FLAG_NON_PAGED, size, 'rvDM');
if (!p) return STATUS_INSUFFICIENT_RESOURCES;
// ... use ...
ExFreePoolWithTag(p, 'rvDM');

// Pool flags
// POOL_FLAG_NON_PAGED              - safe at DISPATCH_LEVEL
// POOL_FLAG_PAGED                  - PASSIVE_LEVEL only
// POOL_FLAG_NON_PAGED_EXECUTE      - rarely needed; HVCI-hostile
// POOL_FLAG_UNINITIALIZED          - skip zero-fill (perf, only if you fill it)

Never use ExAllocatePool / ExAllocatePoolWithTag — flagged as deprecated/insecure since WDK 22000.

IOCTL handling

// Public.h (shared with usermode)
#define IOCTL_MYDRV_DO_THING \
    CTL_CODE(FILE_DEVICE_UNKNOWN, 0x800, METHOD_BUFFERED, FILE_ANY_ACCESS)

// Driver
VOID MyEvtIoDeviceControl(_In_ WDFQUEUE Queue, _In_ WDFREQUEST Request,
                          _In_ size_t OutputBufferLength, _In_ size_t InputBufferLength,
                          _In_ ULONG IoControlCode) {
    UNREFERENCED_PARAMETER(Queue);
    NTSTATUS status; size_t bytes = 0;

    switch (IoControlCode) {
    case IOCTL_MYDRV_DO_THING: {
        PMY_INPUT in; PMY_OUTPUT out;
        status = WdfRequestRetrieveInputBuffer(Request, sizeof(*in),  (PVOID*)&in,  NULL);
        if (!NT_SUCCESS(status)) break;
        status = WdfRequestRetrieveOutputBuffer(Request, sizeof(*out),(PVOID*)&out, NULL);
        if (!NT_SUCCESS(status)) break;
        out->Result = in->Value * 2;
        bytes = sizeof(*out);
    } break;
    default:
        status = STATUS_INVALID_DEVICE_REQUEST; break;
    }
    WdfRequestCompleteWithInformation(Request, status, bytes);
}

Buffer methods: METHOD_BUFFERED (kernel-allocated copy — safest), METHOD_IN_DIRECT / METHOD_OUT_DIRECT (MDL — for big buffers), METHOD_NEITHER (raw user pointer — dangerous, requires ProbeForRead/ProbeForWrite in a try/except).

WPP tracing

// Trace.h
#define WPP_CONTROL_GUIDS \
    WPP_DEFINE_CONTROL_GUID(MyDriverCtl, (00000000,1111,2222,3333,444444444444), \
        WPP_DEFINE_BIT(TRACE_DRIVER) WPP_DEFINE_BIT(TRACE_DEVICE))

// Driver.c — on top of file (after includes)
#include "Driver.h"
#include "Driver.tmh"               // generated by WPP preprocessor

// Use
TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_DRIVER, "DriverEntry: status=%!STATUS!", status);

Decode at runtime: tracelog.exe -start mytrace -guid GuidFile -f .\trace.etl -flag 0xFF -level 5. Decode the .etl with tracerpt or traceview.exe against the driver .pdb.

Object lifetime + cleanup

Every WDF object has a parent. Destroying a parent destroys children. Wire cleanup explicitly when needed:

WDF_OBJECT_ATTRIBUTES attrs;
WDF_OBJECT_ATTRIBUTES_INIT(&attrs);
attrs.EvtCleanupCallback = MyContextCleanup;   // runs at PASSIVE_LEVEL
attrs.EvtDestroyCallback = MyContextDestroy;   // may run at DISPATCH_LEVEL
WDF_OBJECT_ATTRIBUTES_SET_CONTEXT_TYPE(&attrs, MY_DEVICE_CONTEXT);
WdfDeviceCreate(&DeviceInit, &attrs, &device);

Synchronization

PrimitiveMax IRQLUse
WDFSPINLOCK (WdfSpinLockAcquire)DISPATCH_LEVELShort, no blocking
WDFWAITLOCK (WdfWaitLockAcquire)PASSIVE_LEVELCan block; never at DISPATCH
KEVENT, FAST_MUTEXvariesLess common in WDF; prefer the above
WDF execution levels—Set on WDF_OBJECT_ATTRIBUTES.ExecutionLevel for callback serialization

PnP / Power callbacks (subset)

WDF_PNPPOWER_EVENT_CALLBACKS pp;
WDF_PNPPOWER_EVENT_CALLBACKS_INIT(&pp);
pp.EvtDevicePrepareHardware = MyEvtDevicePrepareHardware;     // map resources
pp.EvtDeviceReleaseHardware = MyEvtDeviceReleaseHardware;
pp.EvtDeviceD0Entry         = MyEvtDeviceD0Entry;
pp.EvtDeviceD0Exit          = MyEvtDeviceD0Exit;
WdfDeviceInitSetPnpPowerEventCallbacks(DeviceInit, &pp);

Verification ladder

  1. Compile with /W4 /WX and SAL annotations — fix every warning
  2. Static Driver Verifier (SDV) — IRQL/locking/leak rules, must be clean before release
  3. WDK CodeQL queries (Microsoft's Windows-Driver-Developer-Supplemental-Tools repo) — must-pass set
  4. Driver Verifier at runtime: verifier /standard /driver MyDrv.sys, then reboot and exercise
  5. !analyze any bugcheck on the test machine via WinDbg

Anti-Patterns

Anti-PatternWhy It's BadCorrect Approach
ExAllocatePoolWithTagDeprecated since 22000ExAllocatePool2(POOL_FLAG_NON_PAGED, ...)
KeWaitForSingleObject at DISPATCHBugcheckSchedule a workitem to PASSIVE
try/except around random kernel APIsHides real bugsUse SEH only around user-buffer probing
Unprobed METHOD_NEITHER user pointerBluescreens / privilege escalationUse METHOD_BUFFERED or probe explicitly
Forgetting WdfRequestCompleteI/O hangs foreverComplete on every code path (or WdfRequestStopAcknowledge)
Returning success but completing the request elsewhere asynchronously without marking it pendingDouble-complete bugcheckMark with WdfRequestMarkCancelable and complete once
C++ STL / new / RTTI / exceptionsNot safe in kernel by defaultPlain C, wil:: helpers, LIST_ENTRY intrusive lists
DbgPrint for production diagnosticsNo structured log, no levels filteringWPP tracing
Discovery
Tags

No tags published for this skill.

Version
Latest version metadata

Version

v2026.09.24

Published

Sep 24, 2026

Category

Uncategorized

License

MIT

Source path

skills/windows/wdf-kmdf

Default branch

main

Latest commit

9496306

Tree SHA

fe4e2f1