Skip to content
macOSDeep Dive Published Updated 6 min readViews unavailable

From Kernel Extensions to System Extensions: Why Apple Moved Drivers Out of the Kernel

Apple's multi-year push to deprecate kernel extensions for user-space System Extensions and DriverKit, and what changes when a driver runs outside the kernel.

For most of macOS’s history, third-party drivers — antivirus software hooking into filesystem or network events, VPN clients implementing custom network interfaces, hardware drivers for peripherals — ran as kernel extensions (kexts), loaded directly into the same address space and privilege level as the kernel itself. Apple has spent years pushing developers away from this model entirely, toward System Extensions and DriverKit, which run the equivalent functionality in user space instead.

Why kexts were risky by design, not just by accident

A kernel extension, once loaded, runs with the full privilege and access of the kernel itself — there’s no isolation between a third-party kext’s code and the kernel’s own memory and control structures. This means a bug in a third-party kext (a crash, a memory corruption issue, a race condition) doesn’t just crash that piece of software — it can crash or corrupt the entire operating system, because the kext’s faulty code is executing with unrestricted kernel privilege rather than being contained the way a user-space process’s bugs are contained to that process. Kernel panics traceable to third-party kexts were, for years, one of the more common sources of macOS stability problems that had nothing to do with Apple’s own code.

What System Extensions and DriverKit do differently

System Extensions (covering categories like network extensions, endpoint security, and driver extensions built on DriverKit specifically) run the equivalent functionality as ordinary, isolated user-space processes, communicating with the kernel through well-defined, narrow APIs rather than executing directly inside kernel space. DriverKit specifically provides a user-space framework for writing device drivers — USB, serial, and other peripheral drivers — that historically required kernel-level kexts, now implementable entirely in user space with the kernel exposing only the specific, narrow hardware-access primitives a driver actually needs, rather than full kernel privilege.

Why this actually improves reliability, not just security

The security benefit (a compromised or malicious extension can’t directly control the kernel) is real, but the reliability benefit is arguably just as significant in practice: a bug in a System Extension crashes that extension’s own process, which the operating system can detect and restart, rather than crashing or corrupting the entire kernel. This directly converts what used to be a whole-system kernel panic into an isolated, recoverable single-process crash — the same fundamental benefit that process isolation has always provided for ordinary application crashes, now extended to the driver and low-level extension category that had previously been exempt from it by running in kernel space.

The transition’s actual timeline and pressure

Apple didn’t remove kext support outright in one release — it announced kexts as deprecated well ahead of actually restricting them, giving developers a multi-year runway to migrate to System Extensions/DriverKit equivalents, while gradually tightening the requirements and warnings around loading third-party kexts (including requiring explicit user approval in System Preferences/Settings for any third-party kext to load at all, a meaningfully higher-friction process than kexts had previously required). This staged approach reflects a recurring pattern in how Apple handles platform-level deprecations that would otherwise break a meaningful amount of existing third-party software if done abruptly — announce early, add friction gradually, then eventually restrict or remove.

What this meant for existing third-party software

Vendors of software that had relied on kexts — endpoint security and antivirus vendors being the most affected category, given how deeply that category of software has historically needed to hook into low-level system events — had to substantially rewrite the affected components against the new System Extensions APIs, which don’t offer identical capabilities to what unrestricted kernel-level access provided. Some categories of low-level system hooking that were straightforward from kernel space required entirely different approaches (or, in some cases, simply aren’t achievable at all) from the more restricted user-space System Extensions APIs, which was a genuine source of friction for vendors used to the older model’s flexibility, even as it closed off real attack surface and stability risk in the process.

The broader pattern this reflects

This transition sits alongside several other Apple platform-security moves from roughly the same era — System Integrity Protection restricting what even root can modify, the signed system volume separating the OS itself from user-writable storage, notarization requirements for distributed software — as part of a broader, sustained push to shrink the amount of unrestricted, unsandboxed privilege available anywhere in the system, including to Apple’s own historical extension mechanisms, not just to third-party application code.

How a system extension is actually installed and approved

Unlike a legacy kext, which historically could be installed as part of a normal application install with comparatively little user-facing ceremony, a System Extension requires an explicit, separate user approval step through System Settings — the user sees exactly which extension is requesting to run, from which developer, and must approve it deliberately before it becomes active:

systemextensionsctl list
systemextensionsctl developer on   # for local development/testing only

This explicit-approval requirement is itself a security improvement over the older kext model, where a user might click through a generic installer dialog without ever being shown, specifically and separately, that a piece of software was requesting the ability to load kernel-level or deeply privileged code — System Extensions make that specific request visible and individually revocable, rather than bundled into a broader, less specific installation consent.

Endpoint Security specifically, and why it exists as its own category

Among the three System Extension categories, Endpoint Security deserves particular attention because of what it replaced: security and antivirus vendors had historically relied on the Kauth (Kernel Authorization) kernel API to intercept and evaluate file and process events for real-time threat detection — exactly the kind of low-level, security-critical hooking that’s also the highest-risk category of kext to get wrong. The Endpoint Security framework gives these vendors a structured, user-space API for the same category of monitoring (process execution, file operations, network connections) without needing kernel-level code at all, which is precisely why security vendors were both among the most affected by kext deprecation and among the most directly served by System Extensions’ specific design — Apple built an entire dedicated extension category aimed squarely at replacing their most kernel-dependent historical use case.

What happens to a Mac still relying on a legacy, unmigrated kext

For hardware or software genuinely unable to migrate away from a legacy kext (older peripheral drivers whose vendor stopped maintaining them, for instance), macOS has progressively required an explicit Reduced Security boot policy step on Apple Silicon Macs specifically to permit legacy kexts to load at all — the same boot security policy mechanism covered in this blog’s boot-process deep-dive, applied here as a deliberate friction point discouraging continued kext reliance rather than a permanent, frictionless allowance.

Related:

Sources:

Comments