DriverKit on macOS: Hardware Drivers That Run in User Space
How DriverKit packages, matches, activates, entitles, and isolates user-space driver extensions while preserving controlled access to macOS hardware.
DriverKit moves supported third-party drivers out of the macOS kernel. A DriverKit driver is a driver extension, or dext, built with C++ framework classes and delivered inside a normal application. It can still communicate with hardware and publish services, but a failure occurs in user space instead of corrupting kernel memory.
That isolation improves the failure boundary; it does not turn driver development into ordinary app development. Matching, entitlements, signing, activation, memory ownership, and user-client access remain part of a privileged system contract.
The app is the delivery vehicle
Xcode creates a DriverKit extension target alongside the host app. macOS requires the built dext inside the app bundle at:
Contents/Library/SystemExtensions/
The app and extension have distinct bundle identifiers and entitlements. The host app requests activation through the System Extensions framework. macOS validates the bundle and may require user approval in Privacy & Security before the driver becomes active.
Copying a dext into a legacy extensions directory or loading it with kextload is not a DriverKit installation flow. Updates are delivered with the host app and submitted as system-extension replacement requests. Uninstallation also goes through the supported lifecycle, so removing files behind the system’s back can leave confusing registration state.
Production distribution requires the appropriate Apple-granted DriverKit capability, valid provisioning, Developer ID signing where applicable, and notarization of the containing software. Development modes are for local testing, not instructions to ask customers to weaken System Integrity Protection.
Matching starts the service graph
DriverKit uses an I/O service model. The system discovers a provider for a device, evaluates matching properties from the extension’s configuration, and starts the matching driver service. Family frameworks add hardware-specific APIs: USBDriverKit, HIDDriverKit, NetworkingDriverKit, PCIDriverKit, SerialDriverKit, and AudioDriverKit cover different device classes and platform versions.
A broad match can claim devices the driver does not actually support. Match on the narrowest stable properties supplied by the relevant bus, then validate the device again during startup. Apple’s standard drivers already cover common keyboards, mice, storage, and protocols; a custom dext is intended for proprietary behavior or unsupported device features.
DriverKit classes follow provider-client relationships. The driver starts against a provider, obtains the interfaces it needs, configures queues or memory, and publishes services for clients. Return failures rather than continuing with partially initialized hardware. A user-space crash avoids a kernel panic, but a wedged device, repeated restart, or incomplete firmware transaction still affects the machine.
IIG defines the callable boundary
The IOKit Interface Generator, represented by .iig definitions, describes methods that cross process boundaries. Generated glue handles dispatch and serialization, but the author still owns validation. Treat every scalar, size, memory descriptor, and selector from a client as untrusted.
Bound buffers before mapping them, reject arithmetic overflow, validate states before issuing device commands, and make asynchronous completion safe when the client disconnects. The fact that a caller holds a connection does not prove that each request is sensible.
DriverKit memory objects and dispatch queues replace direct use of many kernel primitives. They also impose lifecycle rules: do not retain a pointer beyond the lifetime of its memory descriptor, and do not assume callbacks arrive on the thread that initiated an operation.
User clients require two-sided authorization
An application that communicates with a dext normally opens a user client. Entitlements constrain which apps may connect. On macOS, the client can carry com.apple.developer.driverkit.userclient-access listing permitted driver bundle identifiers. A driver that permits any user client expands the attack surface and should be exceptional.
Authorization must continue inside the protocol. An entitlement authenticates a code-signing relationship; it does not validate a command’s range, device state, or ownership of an external resource. Version the user-client protocol and reject unsupported requests cleanly so app and driver upgrades can roll forward or back without undefined behavior.
Observe the supported lifecycle
Use systemextensionsctl list to inspect registered state during diagnosis. Capture the activation request result, extension version, matching properties, device registry path, and relevant unified logs. Distinguish four different failures:
- the app never submitted a valid activation request;
- the extension awaits user or administrator approval;
- the dext activated but did not match a device;
- it matched and started, then failed its own initialization.
Those states need different fixes. Repeatedly reinstalling the app cannot correct a wrong matching dictionary, and editing the match cannot satisfy a missing entitlement.
Make upgrades and recovery part of driver design
A dext update changes two coupled contracts: the service that matches hardware and the client protocol used by the host app. Test old app with old dext, new app with new dext, and every transition the installer can expose. If an activation request replaces an active extension, preserve enough version negotiation that a temporarily mismatched client receives a clean unsupported-version error instead of issuing a command under the wrong layout.
Do not assume activation immediately transfers hardware ownership. The old service may still be stopping, the device may need termination and rematching, and the new service can fail its start path. Keep firmware operations restartable, cancel outstanding I/O, release mapped memory and queues in the documented lifecycle callbacks, and make a repeated start safe after partial initialization.
Recovery must not depend on disabling platform security. Ship a signed uninstaller or removal path, retain a known-good host-app build, and document how to inspect extension state when the device is absent. On managed Macs, decide whether approval is delivered through device management and test the first install as a standard user; developer-mode success is not evidence that production approval works.
Finally, test failure containment rather than merely trusting it. Crash the dext during idle, active I/O, client disconnect, sleep, wake, and device removal. Verify that macOS tears down the service, that hardware returns to a defined state, and that the app reports a recoverable driver failure without retrying in a tight loop. User-space isolation pays off only when the surrounding lifecycle can actually recover.
DriverKit is a narrower privilege design, not a shortcut around platform policy. The payoff is substantial: hardware support can be distributed with an app, updated through a managed system-extension flow, and isolated from the kernel while retaining explicit authorization at every boundary.
Related:
- File Provider on macOS: Domains, Placeholders, and System-Managed Sync
- From Kernel Extensions to System Extensions: Why Apple Moved Drivers Out of the Kernel
Sources: