How Ladybird Browser Uses pledge and unveil for Sandboxing Security
Ladybird Browser isolates rendering and system components in separate processes, applying OpenBSD-style pledge and unveil restrictions to limit system calls and filesystem access, with only the UI process running unsandboxed.
Ladybird Browser implements a defense-in-depth security model by combining multi-process architecture with kernel-level sandboxing primitives. According to the LadybirdBrowser/ladybird source code, every non-UI component—from the WebContent renderer to the Network process—runs under strict pledge and unveil constraints that minimize the attack surface available to malicious web content.
Ladybird Browser Sandbox Architecture Overview
Multi-Process Design with Privilege Separation
Ladybird decomposes the browser into specialized processes, each with a minimal privilege set. The architecture separates the Browser process (UI and window management) from WebContent (HTML/CSS/JS rendering), Network (HTTP requests), and ImageDecoder (media processing). This isolation ensures that a vulnerability in the JavaScript engine cannot directly access the network or filesystem.
The Role of pledge and unveil in OpenBSD Sandboxing
pledge is a system call that restricts the types of operations a process may perform, while unveil limits filesystem visibility to specific paths. Ladybird leverages these OpenBSD-native mechanisms (and equivalent implementations on other platforms) to create a two-layer sandbox: first restricting what a process can do, then restricting where it can access data.
How WebContent Process Implements pledge and unveil
The WebContent process—responsible for rendering web pages and executing JavaScript—applies the most restrictive sandboxing constraints immediately upon startup.
Restricting System Calls with pledge()
In WebContent/Process.cpp, the process invokes pledge() early in initialization to whitelist only essential system calls. The typical pledge string for the renderer includes:
pledge("stdio rpath wpath cpath inet unix fattr flock proc exec prot execve", nullptr);
This permits standard I/O, read/write path operations, network sockets (inet), Unix domain sockets (unix), and process management (proc), while explicitly denying access to hardware devices, raw networking, and privileged operations.
Limiting Filesystem Access with unveil()
After restricting system calls, WebContent uses unveil() to expose only specific directories required for operation. As implemented in the source:
// Expose SSL certificate store for HTTPS validation
unveil("/etc/ssl/certs", "r");
// Expose user cache directory for temporary storage
unveil(cache_directory_path, "rwc");
// Lock the filesystem view - no further unveil calls permitted
unveil(nullptr, nullptr);
The permissions string uses OpenBSD conventions: "r" for read, "w" for write, "c" for create, and "x" for execute.
Locking the Sandbox
The final unveil(nullptr, nullptr) call is critical—it permanently locks the filesystem view, preventing any subsequent code (including potentially exploited JavaScript) from expanding the sandbox boundaries. This call appears in all Ladybird subprocess implementations.
Sandboxing Strategy for Auxiliary Processes
Ladybird applies similarly restrictive sandboxes to helper processes, tailored to their specific functions.
Network Process Constraints
The Network process, handling HTTP/HTTPS requests, operates with a reduced syscall surface. In Network/Process.cpp:
// More restrictive than WebContent - no execve, limited proc
pledge("stdio rpath inet unix dns", nullptr);
// Only unveil the SSL cert directory and socket paths
unveil("/etc/ssl/certs", "r");
unveil("/tmp/ladybird_sockets", "rw");
unveil(nullptr, nullptr);
Note the absence of execve and prot promises, preventing the Network process from executing external programs or modifying memory protections.
ImageDecoder and Other Services
The ImageDecoder process uses an even tighter sandbox since it only needs to read image data and write decoded bitmaps:
pledge("stdio rpath wpath", nullptr);
unveil(image_cache_path, "rw");
unveil(nullptr, nullptr);
This minimal surface ensures that a malicious image file cannot exploit the decoder to access the network or filesystem beyond the cache directory.
Privilege Dropping and User Isolation
Before applying pledge and unveil restrictions, all non-UI processes drop root privileges and switch to a dedicated unprivileged user. In Launcher.cpp, the process initialization follows this sequence:
// 1. Drop privileges
Core::System::setuid(ladybird_uid);
Core::System::setgid(ladybird_gid);
// 2. Apply syscall restrictions
pledge(pledge_promises, nullptr);
// 3. Limit filesystem visibility
unveil(required_paths...);
unveil(nullptr, nullptr); // Lock
// 4. Enter main event loop
This three-layer defense—user isolation, syscall filtering, and filesystem virtualization—ensures that even if an attacker bypasses one mechanism, the process remains confined by the others.
Key Source Files and Implementation Details
| File | Purpose | Sandbox Implementation |
|---|---|---|
WebContent/Process.cpp |
Renderer process | pledge() with stdio rpath exec prot..., unveil() for SSL certs and cache |
Network/Process.cpp |
HTTP/HTTPS handling | Restricted pledge() without execve, unveil() for sockets and DNS |
ImageDecoder/Process.cpp |
Image decoding | Minimal pledge("stdio rpath wpath"), cache-only unveil() |
Launcher.cpp |
Process spawning | Privilege dropping via setuid/setgid before sandbox activation |
Documentation/ProcessArchitecture.md |
Architecture overview | Describes the multi-process sandbox model and pledge/unveil strategy |
These files collectively implement Ladybird's security boundary, with each process type applying the minimal set of promises and filesystem visibility required for its specific role.
Summary
- Multi-process isolation: Ladybird separates the UI, rendering, networking, and media decoding into distinct processes to contain potential exploits.
- OpenBSD sandboxing: Non-UI processes use
pledge()to whitelist permitted system calls andunveil()to restrict filesystem access to specific paths. - Privilege dropping: All sandboxed processes switch to an unprivileged "ladybird" user before applying kernel restrictions, creating a defense-in-depth architecture.
- Locking mechanism: Each process calls
unveil(nullptr, nullptr)to permanently freeze its filesystem view, preventing runtime sandbox expansion.
Frequently Asked Questions
What is the difference between pledge and unveil in Ladybird Browser?
pledge restricts which system calls a process can invoke, effectively defining what operations the process is allowed to perform (such as file I/O, network access, or process creation). unveil restricts which parts of the filesystem the process can see and access, regardless of Unix permissions. Ladybird uses both together: pledge limits the attack surface at the kernel interface, while unveil prevents access to sensitive files even if the process is compromised.
Why doesn't the Browser process use pledge and unveil?
The Browser process manages the UI, window system integration, and user interactions, requiring broad access to desktop resources, configuration files, and the display server. Restricting it with pledge and unveil would prevent necessary operations like accessing user downloads, managing window decorations, or integrating with the native file picker. Instead, Ladybird relies on the operating system's standard user privilege model for the Browser process while sandboxing all content-handling subprocesses.
How does Ladybird prevent sandbox escape even if pledge is bypassed?
Ladybird implements a layered defense strategy. First, sandboxed processes drop root privileges and switch to a dedicated unprivileged "ladybird" user before calling pledge or unveil, ensuring they lack OS-level rights even if kernel restrictions fail. Second, the unveil mechanism locks the filesystem view with unveil(nullptr, nullptr), preventing runtime expansion of accessible paths. Finally, the multi-process architecture ensures that compromising one sandboxed component (like the renderer) does not automatically grant access to network or filesystem resources handled by other processes.
Which Ladybird processes use the most restrictive sandbox?
The ImageDecoder process uses the most restrictive sandbox, employing a minimal pledge set of "stdio rpath wpath" that permits only basic I/O and file reading/writing, with no network, process execution, or memory protection privileges. Its unveil access is limited exclusively to the image cache directory, preventing access to SSL certificates, configuration files, or user documents. This extreme restriction is possible because image decoding requires no external resources beyond reading input bytes and writing decoded bitmaps to the cache.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →