freepdfeditor/docs/adr/0004-two-process-trust-boun...

71 lines
3.3 KiB
Markdown

# ADR-0004 — Two-process model with the IPC as a bidirectional trust boundary
* **Status**: Accepted
* **Date**: 2025-07-25
* **Plan reference**: §2.1
## Context
PDF parsers are among the most heavily exploited attack surfaces in desktop
software, and we are writing a new one. A single-process editor means a
parser bug is a direct path to the user's files, network, and other
applications. Sandboxing is the one thing in the plan that is genuinely
painful to retrofit (§14 step 8), so the process split must be decided
before any parsing code lands.
The naive read of "sandbox the parser" treats the sandbox as protecting *us*
from *the document*. The decision recorded here is that the IPC boundary is
a trust boundary in **both** directions: the document process is compromised
by hostile input, so anything it sends is itself hostile to the unsandboxed
UI process. A compromised document process attacking the UI is the obvious
escape path and the one people forget.
## Decision
**Two processes.** The UI process (Qt) holds the canvas, panels, tools,
command dispatch, undo, and render-surface compositing; it does no untrusted
parsing. The document process (sandboxed) holds file I/O, parsing, the object
model, content streams, rasterization, re-emission, and save. One document
process per open document; a crash loses one tab, not the app.
* **IPC**: local socket, Cap'n Proto messages, shared memory for raster
tiles (no copies on the hot path).
* **Sandbox**: seccomp-bpf + user namespaces (Linux), App Sandbox with a
minimal entitlement set (macOS), AppContainer + low-integrity token
(Windows). The document process gets no network access and no filesystem
access beyond fds handed to it by the UI process.
* **Bidirectional trust boundary.** The UI validates every message (bounds,
counts, string encodings, shared-memory extents) and never indexes,
allocates, or size-computes directly from a document-supplied number.
Cap'n Proto arena reader limits are set explicitly, not left at defaults.
* **Fuzz the UI-side deserializer as its own harness** (§8.2) — a
compromised document process attacking the unsandboxed UI is the obvious
escape path and the one people forget.
A crash in the document process is recovered by the UI: restart it and
replay the journal (§6.4).
## Consequences
**Positive.** A parser bug cannot directly reach the user's files or
network. A document-process crash loses one tab, not the app — and is
recoverable. The bidirectional framing means the escape path people forget
is covered by an explicit gate.
**Negative.** The process split has a real cost on open latency (measured at
M0, §14 step 8). IPC adds complexity to every edit and every render tile.
Sandboxing on all three platforms is platform-specific work that has to be
maintained.
**Neutral.** Two windows on the same file share one document process (§6.4).
## Alternatives considered
* **Single process** — rejected; a parser bug is a direct path to the
user's machine.
* **Sandbox only the parser, IPC treated as trusted** — rejected; a
compromised document process attacking the unsandboxed UI is the escape
path people forget (§2.1).
* **Out-of-process renderer, in-process parser** — rejected; the parser is
the larger CVE surface, not the renderer. PDFium (the renderer) is
battle-tested; our parser is new.