Skip to content

FUSE passthrough solutions and status

Miklos SzerediDaniel Rosenberg

Blue TeamThe Linux Foundation28 minAdvancedOpen on YouTube ↗

Evaluates FUSE passthrough solutions and status and evaluates key implications for security practitioners.

Summary

### Overview This session is a technical discussion focused on implementing a pass-through feature in FUSE (Filesystem in Userspace). Because many FUSE filesystems merely pass data through from an underlying filesystem while only modifying metadata or directory structures, routing all I/O through the userspace FUSE server causes significant performance degradation. The discussion contrasts direct file-descriptor pass-through approaches (such as the patches used in Android) with fuse-BPF, exploring architectural challenges around security, visibility, and mapping establishment.

### Key Topics - **Mapping Granularity and Establishment:** Deciding whether mappings between FUSE files and underlying backing storage should occur per whole file, or at block or byte ranges. The discussion also covers establishing mappings during `open` versus during `lookup`. - **Security of File Descriptor Passing:** Examining why passing open file descriptors via standard write replies creates security risks (hijacking writes, administrative command injection) and why `ioctl` is required instead. - **Visibility and Mount Namespace Issues:** Addressing the issue where underlying mapped files become invisible to inspection utilities such as `lsof`, as well as handle leaks and namespace boundary complications. - **fuse-BPF Architecture:** Utilizing BPF programs as pre-filters and post-filters around operations, enabling the kernel to conditionally bypass userspace, filter operations, or modify parameters and return codes without forwarding every request to userspace. - **Upstream Strategy:** Determining whether basic direct pass-through should be merged first into upstream FUSE before layering BPF features on top.

### Technical Details - **Security Rationale for ioctl:** Normal server replies are transmitted via write. However, tricking a privileged program into writing arbitrary data can lead to unintended file descriptor transmission or administrative command execution. Referencing insights from Jann Horn, attendees noted that programs frequently write arbitrary data, whereas triggering a specific `ioctl` requires a much higher exploitation threshold. Fuse implementations use `ioctl` specifically for passing structured mapping data and file descriptors. - **FD Pass-through vs. fuse-BPF:** The direct pass-through approach (used in Android for read/write performance) attaches the backing file descriptor during `open`. Conversely, fuse-BPF attaches mappings to the `lookup` response and links backing inodes/paths for the lifetime of the inode. - **Filtering Hooks in fuse-BPF:** fuse-BPF introduces pre-filters and post-filters. A pre-filter can modify incoming arguments, bypass userspace to directly access the lower backing file, return early errors, or delegate unhandled offsets to userspace. Post-filters can inspect or redact read data and modify return codes. BPF programs must be registered upfront (e.g., via `struct_ops`) before use. - **Visibility Challenges:** Direct kernel mappings decouple the open FUSE file from the underlying backing file in tools like `lsof`. Unlike `overlayfs`, where the mapping is fixed at mount time, FUSE server mappings can point to arbitrary underlying files and cross namespace boundaries, complicating derivation and tracking.

### Takeaways Participants, including Miklos Szeredi, agreed that the cleanest path toward upstream acceptance is to first implement a standalone, direct FUSE pass-through solution without BPF dependencies. Once basic pass-through is in place in upstream FUSE, the more complex fuse-BPF filtering framework can be layered cleanly on top.

Related talks