Skip to content

Repository-wide bug sweep: fail-closed xtask, honest comments, dead configuration removed - #19

Open
MatejGomboc wants to merge 7 commits into
mainfrom
fix/comprehensive-sweep
Open

Repository-wide bug sweep: fail-closed xtask, honest comments, dead configuration removed#19
MatejGomboc wants to merge 7 commits into
mainfrom
fix/comprehensive-sweep

Conversation

@MatejGomboc

Copy link
Copy Markdown
Member

Summary

A comprehensive bug hunt over the whole repository — source, tooling, configuration and documents — run as eight independent review passes, each finding then re-verified by a separate skeptic before anything was changed. Ninety-eight candidate findings were produced; 83 survived verification and are fixed here, 13 are flagged below as yours to decide, and 2 were refuted and dropped.

No kernel logic defect was found. The capability crate and the boot path both came through clean. What the sweep did find, in quantity, is a tree that had drifted from describing itself accurately — which for a project whose thesis is that divergent copies of a truth must be made structurally impossible is the defect class that matters most.

Explanation for the maintainer

Seven commits, each one logical group, reviewable in isolation:

  1. fix(xtask) — the only behavioural code change. boot-test piped QEMU's stderr into a pipe nobody drained: every emulator-side diagnostic was silently discarded, and enough output would have blocked QEMU on a full buffer and surfaced as a timeout blamed on your link script. stderr is now inherited; stdin is nulled so the stdio chardev cannot leave a terminal in raw mode after the unconditional kill; and a QEMU that dies before the first console line now says so with its exit status. Argument parsing fails closed — a flag can no longer swallow the next flag as its value, a forgotten value is an error rather than a silent default, and unrecognised tokens are rejected (--arch=x86_64 previously built aarch64 without a word). Trade-off recorded: an --expect string beginning with -- is now inexpressible; none exists today, and CI's invocations are unaffected.
  2. test(capability) — the forged-handle sweep now denies every forged (index, generation) pair at remove as well as resolve, which is what CHANGELOG.md already claimed of it. Safe inside the existing loop because a failing remove restores slot state before returning. No new test function, so the twenty-seven count still holds.
  3. docs(capability) — three doc comments corrected against the code: StaleGeneration named only the slot-vs-handle cause (it is also what resolve/derive return for a destroyed object — the O-3 half — while the "closed" case it named reports Empty); DUPLICATE pointed at diminish, which never checks it, instead of Capability::derive; union's O-2 note said "narrow" where the risk it dismisses is widening.
  4. fix(kernel) — comments that misdescribe the machine. Two credited "QEMU's firmware" with configuring the PL011, but no firmware runs in the -kernel path at all; the console works because QEMU's device model transmits uninitialised, which is the load-bearing fact a future real-hardware port must not inherit wrongly. The SPSel comments claimed the msr is what keeps an SP_EL0-group entry off the uninitialised SP_EL0 — per the architecture, exception entry already sets PSTATE.SP, the vector group records the interrupted context's stack, and the instruction is defence in depth. Plus build.rs's missing SPDX header.
  5. chore(infra) — configuration that could not do what it said. The aarch64 debug config pointed at the pre-soft-float target directory (a stale binary is still sitting there, so LLDB was silently loading outdated symbols). rust-toolchain.toml instructed bumping a dtolnay/rust-toolchain pin that check-toolchain-pin.sh fails the build over — following the comment would break CI. That script's own hand-install detector was anchored to line starts and so could never match curl … sh.rustup.rs | sh inside a block scalar, the form every workflow here uses. ci_main.yml's checks job had a dead ghcr login (the job never pushes, and ci_pr.yml proves the image pulls anonymously on every PR). .gitignore ignored a GDB history file GDB never writes; .gitattributes' *.S rule never matched this tree's lowercase .s on the case-sensitive filesystems it exists for. Deletion: cleanup_caches.yml and cleanup-caches.js are removed — no workflow uses actions/cache, and the script's header describes another repository's cache groups and a "PR #150 sweep" in a 59-commit repo. Absent beats disabled, which is now one of §3's own graves.
  6. docs (accepted documents) — four mechanical cross-reference corrections, none touching what any document decides, each logged in docs/CHANGELOG.md rather than edited silently. RFC-0003 §11 cited "§13" for the open questions (that is Obligations discharged; they are §14 — a typo present since the draft). RFC-0004 §14 said the scheduler RFC is what "§6 and §9.2 wait on", but §6 is capability transfer with no scheduler dependency; the donation is §4. Threat-model O-18 pointed at §7 for a deferral living in §9, and O-6/§11 attributed to the constitution a module designation it does not make and to RFC-0002 a practice it never states.
  7. docs (contributor documents) — the clippy command in CONTRIBUTING.md, the PR template and STYLE.md was unscoped, linting the kernel for the host triple and never for either Tier-1 target: not what CI runs, and contradicting the same file's own "always name the package" rule. The RFC guidance and /check-primitive still listed four graves after §3 grew to seven, so an audit run from either silently skipped three. README.md still advertised zero-copy page transfer for bulk data — the mechanism RFC-0004 pruned. Four files said the toolchain is pinned in three places. Twenty-nine "CLAUDE.md §N" citations across fifteen files now name CONSTITUTION.md; the PR template's unsafe checkbox goes the other way, since CLAUDE.md is what designates the modules and the constitution only requires that something does.

Which pillar does this serve?

Mostly toil, in the honest sense — but toil that serves §5.2 and §5.4 directly: a document that misdescribes the system teaches its readers to discount the rest of it, and the xtask fixes remove a class of CI failure that lies about its own cause.

Flagged for you, not taken

Thirteen findings are yours to decide. Constitution text and the meaning of accepted RFCs are not mine to change:

Constitution (§3, §7) — the two that matter most:

  • §3 still commends "zero-copy page transfer for large ones (L4)" — the exact mechanism accepted RFC-0004 §5 pruned as L4's abandoned long IPC, and which research/0001 records as "the mechanism seL4, NOVA and Fiasco.OC all removed". Neither 2026-08-01 amendment touched it, so it is now the last doctrinal statement of the pruned design. Suggested: "bulk data zero-copy through shared-memory regions established out of band, IPC carrying only small descriptors. (L4 lesson, as corrected by RFC-0004 §5)". I fixed the same claim in README.md, which is freely editable, so the two now disagree until you rule.
  • §7 names the pinned AArch64 target as aarch64-unknown-none — the pre-soft-float triple the project documented as fatal (FP/SIMD with CPACR_EL1.FPEN = 0, dead before the first character) and replaced. The Dockerfile, rust-toolchain.toml and the pin check all install -softfloat; the constitution names the lethal one.

Accepted RFCs (yours via dated amendment):

  • RFC-0003 §6 asserts derive "records the parent" and "the kernel records" derivation — contradicting the flat table you built, which records nothing and has a test pinning that absence, and contradicting the RFC's own acceptance note deferring derivation records to RFC-0003a.
  • RFC-0003's header lists O-3 unqualified in Discharges, while §13, the acceptance note and the amendment all record it as half-discharged (destruction only).
  • RFC-0004's acceptance note says "the copy-vs-map verdict joins the MMU RFC", but the revised body it accepts had already settled copy-vs-map by pruning map; the open question is shared-memory establishment. docs/CHANGELOG.md:171 faithfully transcribes the same wording, so both want correcting together.
  • RFC-0004 §5 attributes the "~100 cycle" figure to §3 — the constitution has never contained a cycle figure; the original draft correctly credited the C++ blueprint.
  • RFC-0001's graves check says "None of the four apply" — §3 now names seven.
  • Threat model: O-1 and O-2 arguably now qualify as Built by §6's own definition (in tree, exercised by CI — the capability crate is both), yet remain Designed with the ratio restated as three of twenty-seven. Defensible in spirit, since no syscall boundary enforces either at B1 yet; the choice is whether to qualify the statuses or tighten the definition.

xtask judgement calls I took anyway — the three fail-closed parsing changes were verified as "real, but a design decision for the maintainer". I applied them because they sit with the project's fail-closed instincts everywhere else; say the word and I will revert any of them.

Borrow Ledger

  • I checked the Borrow Ledger (CONSTITUTION.md §4) for this subsystem
  • The verdict for this subsystem is: write ourselves (xtask/tooling, capability crate, documents) — no ported code touched
  • Vendored code retains its original licence notices; new files carry an SPDX header — build.rs and the two CI scripts gained the headers they lacked; no vendor/ tree exists yet

unsafe register

  • New unsafe blocks: None. No unsafe block was added, removed or moved; only two descriptive comments in vectors.s changed, and that file is assembly, not Rust.
  • Every new unsafe block carries a // SAFETY: comment (n/a)
  • Every unsafe block is inside a module CLAUDE.md designates for it (unchanged)
  • I have listed them in the session summary / PR description above

Checklist

  • Builds for both Tier-1 targets (aarch64-unknown-none-softfloat and x86_64-unknown-none)
  • Clippy passes — all six scoped invocations CI runs, clean under -D warnings
  • Formatted (cargo fmt --all --check)
  • Toolchain pins agree (bash .github/scripts/check-toolchain-pin.sh)
  • British spelling (bash .github/scripts/check-british-spelling.sh — 65 files)
  • Markdown is lint-clean (markdownlint-cli2 "**/*.md" — 0 issues in 21 files)
  • Boots in QEMU where applicable — no local QEMU on this host; CI's three boots are the gate, including the provoke-exception self-test
  • CHANGELOG.md updated under [Unreleased], and docs/CHANGELOG.md for the document corrections
  • Commit messages follow Conventional Commits (see CONTRIBUTING.md)
  • Every commit GPG-signed

Related

Found by a repository-wide bug hunt on 2026-08-01: eight finder lenses, an independent skeptic pass per lens, and a completeness critic that swept what the lenses missed (SPDX headers, .gitattributes patterns, Cargo.lock, README.md — which no lens had owned).

🤖 Generated with Claude Code

MatejGomboc and others added 7 commits August 1, 2026 15:07
boot-test no longer pipes QEMU's stderr into a pipe nobody drains — it
is inherited, so an emulator-side failure is visible instead of
deadlocking into a bogus timeout — and stdin is nulled so the stdio
chardev cannot leave an interactive terminal in raw mode after the
SIGKILL reap. When QEMU dies before producing console output, the
failure now names the emulator and its exit status rather than blaming
the kernel's link script for a timeout that never elapsed.

Argument parsing fails closed: a flag without a usable value is an
error rather than silently swallowing the next flag or falling back to
a default, and unrecognised tokens (typos, the unsupported --flag=value
form) are rejected instead of ignored. Trade-off recorded: an --expect
string beginning with "--" becomes inexpressible; none exists today.

Seven unit tests cover the pure helpers, so CI's cargo test
--package xtask gate verifies something for the first time.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The changelog's claim that the exhaustive sweep proves a dangling
handle "can neither use nor steal a reused slot's occupant" for both
resolve and remove was ahead of the test, which swept only resolve.
Extend the sweep's else branch so every forged (index, generation)
pair is also denied at remove — safe inside the loop because a failing
remove restores the slot state before returning. The record is now
true as written. The churn test's comment also now says which
assertions run per drawn operation and which after every step, instead
of claiming all four run every step.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
StaleGeneration's doc named only the slot-vs-handle cause: the variant
is also what resolve and derive return when the object itself was
destroyed (the destruction half of O-3), and the "has been closed"
cause it named actually reports Empty until the slot is reused. The
DUPLICATE right now points at Capability::derive, the operation that
actually consumes it, rather than diminish, which never checks it.
Union's O-2 note said it is never applied to "narrow" a held
capability where the risk it dismisses is widening. The compile_fail
doctest's comment no longer claims to catch any bounded Clone impl —
only those bounded by traits an object reference satisfies, which is
the instantiation the kernel will use.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…architecture

Two comments credited "QEMU's firmware" with configuring the PL011 —
no firmware runs in the -kernel boot path (boot.s names the built-in
loader correctly); the console works because QEMU's PL011 model
transmits without initialisation. The x86_64 placeholder claimed
AArch64 needs no stack before its first print, contradicting boot.s,
which installs one before anything else. The SPSel comments in
vectors.s claimed the msr is what keeps an SP_EL0-group entry off the
uninitialised SP_EL0; per the architecture, exception entry already
sets PSTATE.SP to 1, the vector group records the interrupted
context's selection, and the msr is defence in depth. The feature
comment in kernel/Cargo.toml quoted a boot-test invocation that fails
as written (--expect is mandatory). Stale "CLAUDE.md §N" citations now
point at CONSTITUTION.md, where the numbered sections live, and
build.rs gains the SPDX header every other Rust file carries.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The aarch64 debug config in launch.json pointed at the pre-softfloat
target directory, silently loading stale symbols; it now names
aarch64-unknown-none-softfloat. rust-toolchain.toml's header instructed
bumping a dtolnay/rust-toolchain workflow pin that no longer exists and
that check-toolchain-pin.sh itself forbids — it now names the two real
places, and dependabot.yml agrees. The pin check's hand-install
detector was anchored to line starts and so could never match the
idiom it exists to catch (curl ... sh.rustup.rs | sh inside a block
scalar); it is now unanchored. ci_main.yml's checks job dropped a dead
ghcr login: the job never pushes and the cache image pulls anonymously,
as ci_pr.yml already proves on every pull request.

The cache-cleanup workflow and script are deleted: no workflow here
uses actions/cache, the script's header described another repository's
cache groups, and absent beats disabled. .gitignore's /gdb-history
entry matched a file GDB never writes and now ignores .gdb_history;
.gitattributes' *.S force-text rule never matched this repo's
lowercase .s assembly on case-sensitive filesystems and now covers
both spellings. The devcontainer's history volume gains the HISTFILE
that actually makes bash write into it. The two CI scripts gain SPDX
headers, and the stale "CLAUDE.md §N" citations across the config
files now point at CONSTITUTION.md, where the numbered sections live.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
All four are mechanical — a citation pointing at the wrong section or
the wrong document, changing nothing any document decides. RFC-0003
§11 cited the open questions as §13, which is Obligations discharged;
they are §14, as the RFC's own acceptance note and the capability
crate both already said. RFC-0004 §14 said the scheduler RFC is what
"§6 and §9.2 wait on", but §6 is capability transfer and names no
scheduler dependency — the donation that waits on the scheduler is §4.
The threat model's O-18 pointed at §7 for a deferral that lives in §9,
and O-6 and §11 attributed to the constitution a module designation it
does not make and to RFC-0002 a practice it never states.

Logged in docs/CHANGELOG.md rather than edited silently: an accepted
document's body is a record.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The clippy command in CONTRIBUTING.md, the pull-request template and
STYLE.md was unscoped: it lints the kernel for the host triple and
never for either Tier-1 target, so it is neither what CI runs nor
consistent with the same file's rule about always naming the package.
It now points at the one canonical list, which gains the third
workspace member CI has been gating on all along.

The RFC guidance and /check-primitive still enumerated four graves
after §3 grew to seven, so an audit run from either silently skipped
three. README.md still advertised zero-copy page transfer for bulk
data — the mechanism RFC-0004 pruned as L4's abandoned long IPC. Four
files described the toolchain as pinned in three places. The testing
table promised boot-test on both architectures, which xtask refuses
and CI does not do. The feature-request template called Setonix a
credential-handling project, boilerplate from another repository, and
now asks the questions a capability kernel actually needs answered.
The changelog's SPSel claim and CLAUDE.md's soft-float rationale are
corrected to match the vectors the kernel now installs.

Stale "CLAUDE.md §N" citations across fifteen files now name
CONSTITUTION.md, where the numbered sections live; the PR template's
unsafe-module checkbox goes the other way, since CLAUDE.md is what
designates the modules and the constitution only requires that
something does.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
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