From 228d7f994673c4d7eca2ad9947dfeb3f0986b785 Mon Sep 17 00:00:00 2001 From: Nicholas Renner Date: Thu, 2 Jul 2026 11:38:59 -0400 Subject: [PATCH 1/2] threat model --- docs/images/doc-images/actor-topology.png | Bin 0 -> 55626 bytes docs/internal/threat-model.md | 265 ++++++++++++++++++++++ 2 files changed, 265 insertions(+) create mode 100644 docs/images/doc-images/actor-topology.png create mode 100644 docs/internal/threat-model.md diff --git a/docs/images/doc-images/actor-topology.png b/docs/images/doc-images/actor-topology.png new file mode 100644 index 0000000000000000000000000000000000000000..3d94050da0a67fd13a718c4feb6880a84755f6c9 GIT binary patch literal 55626 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 Xp`kh|((k3u@1-EC`l?vk^wa+V!(cIL literal 0 HcmV?d00001 diff --git a/docs/internal/threat-model.md b/docs/internal/threat-model.md new file mode 100644 index 0000000000..b45552a941 --- /dev/null +++ b/docs/internal/threat-model.md @@ -0,0 +1,265 @@ +# Lind-Wasm Threat Model + +## 1. Overview + +Lind-Wasm runs multiple mutually untrusting programs, called *cages*, inside one +unprivileged Linux process. Two layers keep them apart: + +1. **WebAssembly SFI.** Each cage runs in its own Wasmtime `Store` with its own + linear memory, and every memory access is bounds-checked by JIT-emitted code. + A cage cannot touch memory outside its own region no matter what it runs. +2. **RawPOSIX.** A user-space microvisor that handles every syscall a cage makes. + A cage cannot issue a raw Linux syscall. Calls go `make_syscall()` → 3i → + (grate stack) → RawPOSIX → kernel, and RawPOSIX checks each one before + forwarding. + +In between, **3i** routes a cage's syscalls through optional policy components +called *grates* before they reach RawPOSIX. Affecting the host or another cage +means defeating both layers. + +## 2. Methodology + +We do not sort components into trusted and untrusted, because that hides the fact +that a grate is trusted by the cages it isolates and by nothing else. Instead we +ask: + +- What are the actors? +- What is isolated from what? +- For each attacker position, what can it do to each other actor? + +We also do not assume the components we rely on are bug-free. For each one we +state what a bug in it would cost. A bug in RawPOSIX and a bug in the host kernel +are both bad, but their blast radius is not the same. + +## 3. Actors + +The instructive topology is small: + +![Lind-Wasm actor topology](../images/doc-images/actor-topology.png) + +*Figure 1. Color marks trust position: red cages (untrusted), amber grates +(untrusted), green the Lind runtime and TCB, slate the kernel and hardware. The +dashed line is the Wasm SFI boundary, guest above and host below. Cage A routes +through Grate B, its ancestor grate. Cage C and Grate D each sit in their own +separate stack, with their partner boxes drawn lighter, unrelated to A and B and +to each other. RawPOSIX, Wasmtime, and 3i form one band because nothing isolates +them, and the paper's convention puts them at the bottom.* + +| Actor | Role in the model | +| --- | --- | +| **Cage A** | The reference application cage. Fully adversarial. | +| **Grate B** | The ancestor grate of Cage A. Mediates Cage A's syscalls. | +| **Cage C** | An unrelated cage. No routing relationship to A or B. | +| **Grate D** | An unrelated grate. No routing relationship to anything in A's stack. | +| **RawPOSIX + Wasmtime + 3i** | The Lind runtime (TCB). One actor because the three share fate (Section 4). | +| **Host kernel + hardware** | The bottom of the trust stack. | + +Grates are themselves cages under the same SFI. Cage versus grate is a question +of position in the stack, not of isolation mechanism. + +## 4. Trust relationships + +Stated as isolation rather than a label: + +- **Host kernel + hardware.** Assumed correct. Out of scope to defend. +- **Lind runtime (Wasmtime + 3i + RawPOSIX).** The TCB. All three are host Rust + code outside any sandbox, with no isolation between them, so a bug in one is a + bug in all. Their state (handler tables, per-cage vmmap, fdtables, signals) is + host memory the guest cannot address. Compromise it and the attacker owns every + cage and grate, still capped at the unprivileged-process ceiling (Section 7, + Case 4). +- **Grates.** Untrusted code under the same SFI as a cage, never in the TCB. A + grate is trusted only by its own descendant cages, and only to enforce their + policy. See below. +- **Application cages.** Untrusted and fully adversarial, buggy or malicious. + +### TCB versus trusted policy surface + +The TCB is the set of components that break the system's guarantees if +compromised. It is fixed across deployments and small enough to analyze. + +The trusted policy surface is the grates that must be correct for a *given* +deployment's policy to hold. A bad grate can subvert the policy on the cages it +mediates: skip a namespace check, leak data it pulled via +`copy_data_between_cages`, or fake a return value instead of doing the call. It +still cannot touch the TCB, the host, or unrelated cages. + +So a deployment audits two things separately. The TCB once, and each grate on its +own. Grates are small and single-purpose, so one can be checked without reading +the others. + +## 5. Attacker model + +### Compromised cage + +The attacker fully controls one or more cages and can: + +- Run arbitrary Wasm, including exploits against bugs in its own linear memory. +- Read and write any byte of its own linear memory. +- Call any host import with chosen arguments, including `register_handler`, + `make_syscall`, and `copy_handler_table_to_cage`, subject to 3i's interposition + and scoping. +- Call the syscall API with arbitrary inputs. +- Rewrite its own handler table, try to copy in a different one, or try to drive + 3i operations against peer or ancestor cages (scoping denies these). + +It cannot, by the Wasm memory model rather than any policy check, address host +memory, issue raw Linux syscalls, alter JIT output, or reach another cage's +memory. + +Out of the attacker's control: the kernel and hardware, the Wasmtime JIT and its +output, the 3i logic and handler tables, RawPOSIX state, and other cages' memory. + +### Compromised grate + +A grate that is compromised can observe, change, drop, or fake results for any +syscall it mediates, for the cages below it. It cannot read or write cages that +do not route through it (except through `copy_data_between_cages`), corrupt +RawPOSIX, the handler tables, or the runtime, or reach cages above it or beside +it. It runs under the same SFI as any cage. Its reach is the set of cages it +mediates and the 3i API, which is the same API any cage has. + +## 6. The matrix + +Columns are the **attacker**, rows the **victim**. Each cell is what the attacker +can do to the victim. + +| Victim \ Attacker | Cage A | Grate B | Cage C | Grate D | Runtime | Kernel+HW | +| --- | --- | --- | --- | --- | --- | --- | +| **Cage A** | N/A | CC | RE | RE | KO | KO | +| **Grate B** | FE | N/A | RE | RE | KO | KO | +| **Cage C** | RE | RE | N/A | RE | KO | KO | +| **Grate D** | RE | RE | RE | N/A | KO | KO | +| **Runtime** (RawPOSIX/Wasmtime/3i) | U | U | U | U | N/A | KO | +| **Kernel + HW** | U | U | U | U | U | N/A | + +Legend: + +- **CC** Complete control: read and write the victim's memory, rewrite its + syscalls, change its policy. +- **FE** Denial of service on the grate, plus a direct input channel into it. The + cage can flood the grate that mediates it and hand it malformed calls. Against a + correct grate this is only DoS: memory isolation holds and nothing is + compromised. It differs from RE because the cage routes through this grate, so + this is the one place a grate's code takes cage-controlled input directly, where + a grate bug would surface (Case 2). +- **RE** Resource exhaustion and performance interference. Memory isolation holds. +- **KO** Complete compromise, capped at the unprivileged-process ceiling + (Section 7). +- **U** Unaffected. No path from attacker to victim. +- **N/A** Same actor. + +## 7. Cell reasoning + +Six cases cover the whole matrix. + +**Case 1: ancestor grate against its descendant cage (CC).** Grate B mediates +Cage A, so a compromised Grate B controls Cage A completely: read and write its +memory, rewrite its syscalls (`open` of A becomes `open` of B, `fork` becomes +`exit`), change its policy. This is a grate's normal authority over the cages it +mediates, turned hostile, and it is still capped at the unprivileged-process +ceiling (Case 4). + +**Case 2: descendant cage against its ancestor grate (FE).** Cage A's calls run +through Grate B, so it has reach an outsider does not. It can flood Grate B +through 3i and feed it crafted calls hoping to trip a bug in the grate. Against a +correct grate this amounts to denial of service and nothing more: memory +isolation holds and the grate is not taken over. A buggy grate is the exception, +since this is the one place a cage's input reaches a grate's code directly. That +direct channel is what separates FE from RE, where an unrelated cage has no path +to the grate at all. + +Isolation holds here because of how pointer arguments cross the boundary. A +pointer a cage hands to a grate is never dereferenced blind. Either it travels +down tagged with its originating cage and RawPOSIX faults if it falls outside +that cage's address space, or the grate pulls the bytes in with the +copy-from-cage call, which checks the same provenance before copying. A cage +cannot turn a bad pointer into a memory-safety bug in a grate. + +**Case 3: any unrelated pair (RE).** Two parties with no routing relationship, in +either direction, can only compete for shared resources: CPU, file descriptors, +and the like. Memory isolation holds, and neither can read the other's memory or +touch its policy. + +**Case 4: the runtime as attacker (KO).** A memory-safety bug in Wasmtime, 3i, or +RawPOSIX that an attacker rides into the runtime compromises every cage and grate +(the three share fate, Section 4). Even then the attacker is just an unprivileged +Linux process. Nothing Lind adds sits in the kernel or hardware, so the worst its +own code can cost you is whatever an unprivileged process can do. That ceiling is +the main resilience argument for the design. + +**Case 5: cage or grate against the runtime or kernel (U).** Neither has a path +downward. The runtime's state is host memory it cannot address (Section 4), and +the only way up is a runtime bug, which is Case 4. So these cells are unaffected. + +**Case 6: kernel or hardware compromised (KO, out of scope).** If either falls, +everything is lost and Lind cannot prevent it. It sits in the matrix only to keep +the assumption visible. + +## 8. Security properties + +**P1: cross-cage memory isolation.** No cage can read or write another cage's +linear memory. Each cage's memory is a separate mapped region, and every load and +store is bounds-checked by JIT code against that cage's own `VMMemoryDefinition`. +This holds against arbitrary hostile code. + +**P2: syscall mediation.** No cage issues a raw Linux syscall. Everything goes +`make_syscall()` → 3i → grate stack → RawPOSIX → kernel, and RawPOSIX validates +pointer arguments against the calling cage's vmmap before forwarding. + +**P3: directional confinement.** Syscalls run through the grate stack toward +RawPOSIX, and a cage cannot reach past, around, or below the grates that mediate +it. + +**P4: handler-table integrity.** The 3i logic and per-cage handler tables are +host memory, unreachable from any cage (Section 4), so routing cannot be tampered +with even if the 3i policy logic has a bug. A cage influences routing only +through the declared 3i import API, which is itself open to ancestor-grate +interposition. + +**P5: fork inheritance.** A forked child inherits its parent's handler table +through `copy_handler_table_to_cage`, so a confined cage cannot spawn a child that +escapes its policy. An ancestor grate can intercept the fork to constrain the +child further. + +**P6: control-flow integrity.** Wasm's structured bytecode rules out arbitrary +native jumps, indirect calls are type-checked and table-bounded, and return +addresses live on Wasmtime's native stack rather than in cage-addressable memory, +so a cage's loads and stores cannot reach them. RawPOSIX also refuses +`PROT_EXEC` on any `mmap` or `mprotect` that also asks for `PROT_WRITE`, closing +the one legitimate path to a writable-executable region a cage could run injected +code from. + +### How 3i enforces P3 + +Three properties of 3i close three different escapes: + +1. **Handler changes are interposable.** `register_handler` and + `copy_handler_table_to_cage` go through 3i themselves, so an ancestor grate can + see and deny a cage's attempt to rewrite the routing over it. A cage cannot + quietly change the rules it runs under. +2. **3i operations are scoped to descendants.** A grate can act only on cages + below it, never a parent, sibling, or stranger. 3i enforces this in host code, + so it holds even if a compromised grate learns another cage's ID. +3. **Dispatch itself is not interposable.** `make_syscall`, the primitive + everything else builds on, runs in trusted host code and cannot be hijacked. + Interposition changes what a handler does, not which handler runs. + +## 9. Out of scope + +Not addressed here: + +- **Speculative execution and other microarchitectural side channels.** +- **Memory safety inside a cage.** Corruption confined to a cage's own memory + breaks no cross-cage guarantee. We lean on the underlying SFI model for in-cage + safety and do not reprove it. +- **Wasmtime correctness.** The JIT and its output are assumed correct, and P1 + and P6 rest on this. +- **Compiler and glibc correctness.** + +### Resource exhaustion + +Resource exhaustion is **modeled but not mitigated**: it is the residual in every +cross-party cell of the matrix. The claim is only that isolation and policy +confinement survive it. Fair sharing of CPU, file descriptors, and the like +across cages is future work, not a current property. From 54a1231e5ef41139822b9a06b586e354a754874d Mon Sep 17 00:00:00 2001 From: Nicholas Renner Date: Thu, 2 Jul 2026 11:42:07 -0400 Subject: [PATCH 2/2] docs: register threat model page in mkdocs nav --- mkdocs.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/mkdocs.yml b/mkdocs.yml index 6890b91332..e62fc49c66 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -25,6 +25,7 @@ nav: - Multi-Processing: internal/multiprocess-support.md - Signals: internal/signals.md - Memory: internal/memory.md + - Threat Model: internal/threat-model.md - Contributing: - contribute/index.md - Development setup: contribute/dev-container.md