Can Linux Keep a Microkernel Boundary?

Linux is a monolithic kernel, but that does not mean every component must be trusted in the same way. The subject of this series is kSFS, a research framework that asks whether Linux can run file-system code inside the kernel while still placing a meaningful security boundary around it. The practical question is not simply whether code lives in kernel space or user space. It is whether the interactions crossing that boundary are narrow, validated, recoverable, and fast enough to be useful.

The kSFS paper answers with a carefully bounded proposal. It reuses the FUSE protocol as the shape of the interaction, places the file-system implementation inside an in-kernel WebAssembly sandbox, and optimizes the data path so that the protection does not reproduce all of ordinary user-space FUSE's cost. To judge that compromise, a reader needs more than a list of components. They need to understand what kSFS treats as untrusted, what it promises to contain, and what its measurements still leave open.

The boundary is the subject

A microkernel is usually associated with an operating-system architecture in which services such as file systems run outside a privileged core and communicate through controlled interfaces. kSFS does not turn Linux into that architecture. Its narrower idea is to repurpose one of Linux's existing microkernel-like interfaces for a component that normally has broad kernel access.

That distinction matters. A boundary is not just a line drawn around a process. It is a contract about what crosses the line and who validates it. In kSFS, a file-system implementation should not receive the sprawling, pointer-rich interface that ordinary in-kernel file systems use. Instead, the implementation communicates through operations shaped like FUSE requests. The kSFS driver mediates those operations, while WebAssembly constrains the code's memory and control flow. The module can then run near the kernel's fast path without becoming indistinguishable from the trusted kernel around it.

The pieces have different jobs:

  • The FUSE-shaped interface limits and structures interaction with Linux's file-system machinery.
  • The WebAssembly sandbox limits what the implementation can do with memory and control flow.
  • The capability-based resource policy limits which files or storage resources the instance can access.
  • The kernel-side recovery logic addresses faults and stalls that a user-space operating system process could handle by simply being killed.
  • The performance layer reduces copying, context switching, and unnecessary serialization.

Confusing these jobs produces a misleading security story. WebAssembly's sandbox does not decide which disk a file system should mount. A capability policy does not catch every invalid memory access. An optimized request path does not make the implementation correct. kSFS is an arrangement of mechanisms whose guarantees overlap in useful ways but are not interchangeable.

Consider what happens when an application asks to read a file. Linux still decides that a read should be issued and still owns the kernel-side request state. The filesystem implementation receives the operation through the constrained protocol rather than receiving a pointer into arbitrary kernel structures. It can interpret the request using its own sandboxed memory and ask for data from the resource it was authorized to use. The driver validates that exchange and returns the result to Linux. That division of ownership is the practical meaning of the boundary: the implementation can provide filesystem behavior, but it does not become the owner of the kernel's surrounding state.

The distinction also explains why the storage capability matters. If a faulty implementation can only damage the partition it serves, the failure domain is smaller than if it can reach every mounted filesystem or the kernel allocator. That is still a serious failure—data loss is not a harmless outcome—but it is a different class of failure from corrupting the trusted kernel or taking down unrelated services. kSFS's claim is about making that difference enforceable.

The WebAssembly security model provides an important foundation. Modules execute in an isolated environment; function indexes and indirect-call signatures are checked; call stacks are protected; and accesses to linear memory are bounds-checked. Invalid operations can trap rather than silently corrupt the host. But the official WebAssembly security documentation also makes the limits clear: race conditions, time-of-check to time-of-use bugs, timing side channels, and some function-level code-reuse attacks remain possible. WebAssembly supplies fault isolation, not a complete file-system security policy.

The boundary kSFS wants is therefore best described as microkernel-like, not as a complete microkernel. It preserves a distrust-oriented interaction contract while keeping the implementation in kernel space. That is a more modest claim, but it is also a more testable one.

Why file systems make the question real

File systems are a revealing place to try this experiment because they combine high privilege with unusually complicated behavior. They interact with the virtual file system layer, page cache, block devices, locks, metadata, and user-visible semantics. A bug is not confined to a neat library boundary if the implementation can directly manipulate the shared state used by those subsystems.

The kSFS paper describes this as a central difficulty in Linux. The ordinary in-kernel interface assumes that a file-system implementation is trusted and follows the expected semantics. It exposes many symbols and complex data structures, including structures shared across components. Validating every interaction under a hostile or merely buggy implementation would be difficult. The problem is not only that file-system code contains memory bugs. It is that the surrounding interface was designed to let trusted code participate deeply in the kernel.

The paper's vulnerability table gives the motivation concrete weight: it reports 72 CVEs for ext4 since mainlining, alongside substantial counts for other Linux file systems. Those figures belong to the paper's analysis and should not be read as a current, exhaustive census. Their role is to show why file systems remain a consequential isolation target even when they are mature and widely deployed.

FUSE demonstrates the value of moving the implementation out of the kernel. The Linux FUSE documentation describes a framework in which an ordinary user-space process supplies file-system data and metadata through the kernel interface. It also documents protections for non-privileged mounts: device files are blocked, set-user-ID and set-group-ID behavior is restricted, mount points are checked, and access by other processes is controlled. These rules exist because the filesystem daemon is not assumed to be a fully trusted kernel component.

But user-space placement solves one problem by creating another. Every operation must cross between the kernel and the daemon. The kernel queues a request; the daemon reads it, performs the operation, and writes a response; the waiting kernel path resumes. The documented flow is simplified, and it has difficult failure cases. A daemon can stop responding, requests can deadlock through reentrant access, and a malicious or broken implementation can delay operations. FUSE provides abort mechanisms precisely because isolation does not make liveness irrelevant.

The kSFS paper's performance comparisons add a second cost: system calls, mode and context switches, serialization, and data copies. For workloads that perform many small or random operations, those crossings can dominate useful work. The result is a genuine architectural tension. Native Linux file systems are fast partly because they share the kernel's execution environment, but that shared environment expands the blast radius. User-space FUSE narrows the blast radius, but the boundary becomes expensive to cross.

FUSE's own failure controls make the trade-off more precise. The documentation describes a connection that persists until the daemon dies or the filesystem is unmounted, and it provides ways to abort a connection when requests are stuck. It even distinguishes ordinary broken behavior from malicious deadlock. Those details show that a boundary is useful only when the trusted side can regain control after the untrusted side stops cooperating. kSFS must preserve that principle while replacing the daemon's process boundary with an in-kernel execution boundary.

The cost is also workload-shaped. A large sequential transfer may amortize the fixed work of a request exchange, while a workload such as random reads can repeatedly pay for small operations and data movement. This is why the central performance question cannot be answered by asking whether WebAssembly code is intrinsically fast. The relevant question is whether the complete path—from Linux request, through validation and sandbox entry, to storage and back—does less unnecessary work than user-space FUSE while retaining stronger isolation than native code.

Other remedies address only pieces of the tension. eBPF provides a strongly checked way to run code in the kernel, but the restrictions that make it safe also limit expressiveness. Large, stateful file systems need richer control flow and data structures than a narrowly bounded extension model is meant to support. Memory-safe languages can prevent important classes of memory errors, but they do not by themselves guarantee that kernel code will return promptly, avoid resource exhaustion, or preserve the investment in existing C implementations. A safer language and a safe boundary are related choices, not the same choice.

How to judge the compromise

The useful question is now more precise: can kSFS keep the authority of the kernel focused while moving enough execution close to the kernel to recover practical performance? A meaningful answer needs several tests at once.

First, the file-system implementation must not be able to read or write arbitrary kernel memory. Second, invalid control flow and stack misuse must not become a route into trusted kernel code. Third, a buggy implementation must not crash or indefinitely stall the kernel. Fourth, it must be confined to the storage resources authorized for its instance. Fifth, these protections must preserve enough compatibility and throughput to justify using the design rather than ordinary FUSE.

These tests also define what success would not mean. Containing a file-system fault does not guarantee that the file system maintains correct metadata or preserves the data on its own partition. The paper explicitly puts consistency and integrity outside kSFS's threat model and acknowledges that a sandboxed implementation can still cause data loss on the resource it controls. Nor does the claim cover a compromised WebAssembly runtime, kSFS driver, AOT compiler, or incorrectly instrumented generated code. Those components remain trusted.

That trust boundary is not a footnote. A sandbox can be only as reliable as the mechanism that checks its memory, handles its exceptions, and maps its resource requests. The prototype requires authorized users to load code and currently expects the ahead-of-time compiler to produce correctly instrumented native code; the kernel does not independently verify the module. In a deployment decision, that makes code provenance and maintenance of the runtime part of the security model. The design reduces the trusted surface exposed to each filesystem implementation, but it does not eliminate a trusted computing base.

The relationship can be summarized like this:

flowchart LR VFS["Linux VFS and trusted kernel subsystems"] -->|validated requests| B["FUSE-shaped kSFS boundary"] B --> W["In-kernel WebAssembly sandbox"] W --> F["Untrusted file-system implementation"] W -->|capability-checked access| D["Authorized storage resource"] F -.->|faults remain contained| W B -.-> P["Lower crossing cost while retaining isolation"]

The diagram's most important feature is not the box labeled WebAssembly. It is the chain of responsibilities. Linux still owns the surrounding file-system path. The FUSE-shaped boundary determines how requests enter the untrusted component. WebAssembly limits the component's execution domain. The capability policy limits its resource domain. The performance work tries to shorten the cost of the boundary without deleting it.

That is the standard kSFS must meet. The rest of this series examines why the Linux file-system interface is so hard to trust, what FUSE already gets right, how kSFS adapts WebAssembly to kernel conditions, and how zero-copy execution changes the cost model. The eventual judgment will depend not on whether one benchmark approaches native Linux, but on whether the measured middle ground is worth the additional trusted machinery and the limits it openly retains.

References