Skip to content

v0.2.0 — V10 floor, UDP by default, and evidence for the claims - #1

Draft
eslerm wants to merge 1 commit into
hexproofdev:mainfrom
eslerm:release/v0.2.0
Draft

eslerm wants to merge 1 commit into
hexproofdev:mainfrom
eslerm:release/v0.2.0

Conversation

@eslerm

@eslerm eslerm commented Aug 3, 2026 •

Copy link
Copy Markdown
Contributor

Summary

  • BREAKING: hard Landlock floor raised V7 → V10 (kernel 7.2). V10 is in the floor for the UDP net rights — beneath it Landlock cannot restrict UDP at all, so DNS egress could only be disclosed as unhandled, never gated. RESOLVE_UNIX (V9) is in it because Landlock has no hook on connect() to a pathname AF_UNIX socket below V9, so no filesystem tier can gate docker.sock or ssh-agent there.
  • BREAKING: UDP restricted by default in both built-in profiles. balanced grants outbound :53 only; strict grants none (names do not resolve under strict). Both now report unhandled=- and pass --require-full-coverage.
  • Ring 1 comes from the maintainer's fork, pinned by revision. The V10 UDP rights are in no published landlock release. The fork is pinned at a rev verified file-by-file against the tree it replaced (one file differs: src/net.rs, eleven doc-comment lines). Cargo.lock carries a real source and commit SHA; deny.toml allows that source explicitly. Consequence stated plainly: oring cannot be published to crates.io, and today the floor admits only a self-built mainline kernel.
  • SECURITY (both MISREPORT, not BREACH): a decoyed $HOME could silence the shared_cache_write= disclosure (now unions the passwd home with $HOME); caller-supplied bytes reached the terminal unescaped at 13 of ~14 diagnostic sites, making a forged SEALED line injectable through argv (closed by crate::diag::safe and the OringError::Display chokepoint, T26).
  • EVIDENCE: NO_NEW_PRIVS and the FD scrub measured for the first time; the containment matrix runs every denial cell BARE first and reports UNATTRIBUTABLE rather than PASS where the environment supplies the same denial — 21 of 21 attributed.
  • DOCS: docs/DESIGN.md split by reader job (section numbers unchanged, every DESIGN §n citation still resolves); correction history moved to docs/CORRECTIONS.md; CONTRIBUTING.md and CODE_OF_CONDUCT.md added.

Full detail in CHANGELOG.md.

Verification

CI is the make targets, but no hosted runner is at the V10 floor — CI checks format, lints, dependency policy, the static build, the SMT gate and the fail-closed refusal, and witnesses no containment. make check on a ≥7.2 host is the only thing that does.

BREAKING: hard Landlock floor raised from V7 (kernel 6.15) to V10 (kernel 7.2).
oring refuses on anything below it. V10 is in the floor for the UDP net rights;
beneath it Landlock cannot restrict UDP at all, so DNS egress could only be
disclosed as unhandled, never gated. RESOLVE_UNIX (V9) is in it because Landlock
has no hook on connect() to a pathname AF_UNIX socket below V9, so no filesystem
tier can gate docker.sock or ssh-agent there.

BREAKING: UDP is restricted by default in both built-in profiles. balanced
grants outbound :53 only; strict grants none, so names do not resolve under
strict. Both now report unhandled=- and pass --require-full-coverage.

Two costs, stated plainly rather than discovered: kernel 7.2 is not released, so
today the floor admits a self-built mainline kernel and nothing else; and the
V10 UDP rights are in no published landlock release, so ring 1 comes from the
maintainer's fork -- a git dependency pinned by REVISION, not a branch, so the
crate that installs the cage cannot move under a build -- and oring cannot be
published to crates.io, which rejects git dependencies.

Ring 1 is not vendored. The fork is pinned at a rev verified file by file
against the tree it replaced: one file differs, src/net.rs, by eleven lines, all
doc comments. Cargo.lock carries a real source and commit SHA where the previous
path patch carried neither -- better identification of an input that RustSec
still cannot scan, since [advisories] is keyed to registry packages. deny.toml
allows that source explicitly rather than silently. The cost is the hermetic
clone: a build now fetches the pinned revision.

SECURITY. A decoyed $HOME could silence the shared_cache_write= disclosure: the
field read $HOME and nothing else, so HOME=/tmp/decoy made the seal line report
`none` over a policy granting write on the real ~/.cargo/registry. It now takes
the union of the passwd home and $HOME. And caller-supplied bytes reached the
terminal unescaped at 13 of ~14 diagnostic sites, so a forged SEALED line was
injectable through argv alone. Both are MISREPORT rather than BREACH — the cage
was never affected, the operator's view of it was.

EVIDENCE. NO_NEW_PRIVS and the FD scrub are measured for the first time, the
latter at the boundary where an off-by-one would live. No shipped profile may
reach a host credential store. The containment matrix now runs every denial cell
BARE first and reports UNATTRIBUTABLE rather than PASS where the environment
supplies the same denial — 21 of 21 attributed, replacing a hand-check that had
already gone stale. Three documented controls are demonstrated against the
residuals they are named for, each with a vacuity arm.

DOCS. docs/DESIGN.md was split by reader job; section numbers did not change, so
every DESIGN §n citation still resolves through an index. Correction history
moved to docs/CORRECTIONS.md. CONTRIBUTING.md and CODE_OF_CONDUCT.md added.

CI is the make targets. No hosted runner is at the V10 floor, so CI checks
format, lints, dependency policy, the static build, the SMT gate and the
fail-closed refusal — and witnesses no containment. `make check` on a >=7.2 host
is the only thing that does.

Full detail in CHANGELOG.md.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant