fix(runner): grant the lease sandbox write on the device files it writes
Every lease on the development runner died at `wall_seconds=0` with zero
tokens, `runner_crash` / `AccessDenied`, while the self-test reported
`all_ok=true checks=6`. Traced on the host, not inferred:
openat(…, "…/skills", O_RDONLY|O_DIRECTORY) = -1 ENOENT
open("/dev/null", O_RDWR|O_LARGEFILE) = -1 EACCES
open("/etc/hosts", O_RDONLY|O_CLOEXEC) = 3
open("/etc/resolv.conf", O_RDONLY|O_CLOEXEC) = 3</run/systemd/resolve/…>
openat(3</tmp>, "curl_headers_….tmp", O_WRONLY|O_CREAT|O_EXCL) = 4
open("/dev/null", O_RDWR|O_LARGEFILE) = -1 EACCES
unlinkat(…, "/tmp/curl_headers_….tmp", 0) = 0
`/dev` rides `LANDLOCK_FLOOR_RO_PATHS`, whose mask carries no WRITE_FILE, while
bwrap's `--dev` builds a devtmpfs where `/dev/null` is writable. The engine's
model transport spawns `curl` and wires an ignored stdio stream through that
node, so the policy layer refused what the mount layer allowed — the third
instance of that divergence, after the resolver bind and the child's HOME.
`LANDLOCK_FLOOR_RW_FILES` grants it per FILE at `FILE_ONLY_ACCESS`, additive on
top of the floor's directory read. Not a write grant on `/dev`: that would
cover every node `--dev` builds, and nothing else in that devtmpfs is written.
The class, not just the instance. `CHECK_TRANSPORT` was green throughout,
because executing the binary the engine spawns and wiring its stdio are
different permissions and only the first was measured. An eighth probe check
opens the writable device set for writing under full lease hardening, from the
one list `applyPolicy` grants from, so a set that grows is graded without a
second edit. Read-write is the whole check — read-only passes under the exact
mask that produced this.
`landlock.zig` splits at 350 lines (RULE FLL) along the seam it already had:
`landlock_policy.zig` declares what a lease may touch and asserts those sets,
`landlock.zig` keeps the syscall mechanics that hand them to the kernel.
Proofs: a fork applying ONLY the filesystem wall opens the set read-write, so a
refusal names landlock and not seccomp or the mounts (Linux lane); the grading
tests pin that a refused open fails under every posture, that an executable
transport does not vouch for it, and that an older probe's silence is read as a
failure rather than a pass.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> K
Kishore Kumar committed
afd2ed89493478b7f20c83e5b5bb8eb95c571eb6
Parent: 3efa2c8