fix(kernel): contain syscall user-memory faults - #952
Conversation
Centralize directional copyin/copyout behind caller-VAS validation and ARM abort fixups, migrate syscall pointer families, and preserve kernel-owned state when output transfers fail. Add host rollback coverage and a real QEMU permission/fault matrix with a raw fixup probe and unrelated-kernel-fault negative control.
Reject signal-stack underflow without context mutation, return EFAULT for invalid futex words, and keep UDP-only recvfrom output validation out of the TCP ABI.
forkwright
left a comment
There was a problem hiding this comment.
Independent semantic review verdict: APPROVE at exact head 317f478162a6c9570f7b230040ddf8739bd00f7c against base a6f855bcc54b2083d05b274f4aa2a3654dadb4a6. GitHub cannot record a formal approval because the shared automation login is also the PR author; this is the immutable-head reviewer verdict.
I independently reviewed the security-sensitive diff and its callers. The live-VAS walk enforces the complete overflow-checked range plus direction-specific PL0 permission; the ARM transfer boundary uses only the two labelled LDRBT/STRBT PCs for recovery, while unrelated PL1 aborts remain fatal. The migrated fd, pipe, socket, futex, time, IPC, exec, and signal paths keep user pointers inside bounded copy helpers. Stateful reads defer queue/OFD consumption until copyout succeeds, pipe publication rolls back, futex keys include the caller page table, and signal copy faults preserve the outer active exception frame.
Independent evidence on Nomos at this exact SHA:
env -u CARGO_TARGET_DIR scripts/kernel-host-tests.sh: 2,571/2,571 default and 2,575/2,575 debug-console passed; one intentional skip in each configuration.env -u CARGO_TARGET_DIR scripts/witness/uaccess.sh: PASS with raw fault-fixup recovery, direction split, PROT_NONE/unmapped/cross-page rejection, syscall containment, and continued service-loop progress.- Both fmt checks, changed-shell syntax, and
git diff --checkpassed; the review worktree is clean. - All hosted required checks are green, including kernel i686 tests/ARM build/QEMU chain, workspace tests, clippy/gates, audit/deny/OSV, and rustfmt.
The disclosed residuals are accurate and bounded: UDP source-address and payload outputs are not pairwise byte-atomic under a late second transfer fault, but the datagram is retained; /dev/urandom volatile PRNG state can advance on a late copyout failure while file offsets remain unchanged; and the existing four-register/address-length/envp ABI limitations are not newly claimed as implemented. None reopens the caller-VAS isolation, privileged-fault containment, or kernel-state commit boundary accepted here. No hardware fidelity is claimed.
No blocking finding.
forkwright
left a comment
There was a problem hiding this comment.
Independent integration verdict: APPROVE at exact head a1e44426a3340eff9f0e1f3b7ffad9ff0e78c0d8 on base edd574d43d8574c23501428d78f56c4c4527cd30.
I verified the remote ref, exact parents (317f478162a6c9570f7b230040ddf8739bd00f7c + edd574d43d8574c23501428d78f56c4c4527cd30), and tree 8de7afcc324169f4faaba24405377fffe169761b. Compared with Git’s synthetic merge, this tree differs only in the two intentional semantic resolutions: the canonical runner now orders boot watchdog kfault uaccess ..., and the extraction guard inventories both watchdog and uaccess.
Every shared/adjacent seam retains both parent invariants: CI contains watchdog then the unrelated-kernel-fault negative control then uaccess; Cargo/main retain all three watchdog probes plus uaccess production/QEMU exclusions; the timer IRQ keeps watchdog observe/decide/pet-or-withhold while the data-abort path keeps exact-PC uaccess recovery ahead of ordinary fatal handling; clippy pairs all four probes with QEMU; and the target ledger retains both witness families without hardware overclaim.
Independent exact-tree evidence on Nomos:
- both fmt scopes,
git diff --check, changed-shell syntax, witness extraction, target ledger, and wiring inventory passed; scripts/kernel-clippy.shpassed all 13 configurations, including the three watchdog probes and uaccess;scripts/witness/watchdog.shpassed controlled reset, frozen-owner expiry, and immutable hung-shutdown expiry;scripts/witness/kfault.shpassed with expected rc 4;scripts/witness/uaccess.shpassed with the modeled watchdog initialized before raw LDRBT/STRBT fixup recovery, full permission matrix, and continued service-loop progress;- generated root transcripts were removed and the worktree was clean.
The message-only amend from the locally tested merge preserved the exact tree and parents, and its chore(main): ... subject passes the canonical grammar. The push used the documented #756 exception only after exact main/branch Kanon parity (163 findings, 28 suppressed, strict-equivalent 191); no branch-specific gate failure was bypassed.
No integration blocker. New-head hosted checks are still being monitored. The existing physical AGM M7 watchdog receipt remains explicitly unverified.
|
Hosted exact-head receipt complete for Remote head remains exact, the review worktree is clean, and GitHub now reports |
## Summary - run the canonical source-only wiring inventory check inside the branch-protection-required kernel context before the docs-only exemption, with checkout available on every path - remove the duplicate check from unrequired rustfmt while preserving the guarded full post-boot checker - add one deterministic topology regression that invokes the canonical checker against a malformed isolated inventory and proves later build/QEMU steps remain docs-only guarded - refresh the kernel-core inventory row to describe the uaccess work landed by #890/#952 and #868, including the remaining non-atomic/state/ABI residuals rather than treating #871 as a live owner ## Rationale The admission property belongs to the already-required kernel context: a malformed docs-only inventory must make that context red without paying for kernel build or QEMU. The regression checks workflow composition and reuses `scripts/check-wiring-inventory.sh --no-log`; it does not create a second inventory checker. The general closed-owner freshness mechanism is already tracked by [Kanon #3709](forkwright/kanon#3709), so this repair corrects the stale row without adding a competing Thumos mechanism. ## Verification Exact remote head: `739d67173f040cd5fc7ab173d9b8cb1bf27e08c2` At the authored repair commit, the ordinary pre-push hook passed fmt, workspace check, fitness, cargo-deny, workspace clippy, and nextest (997/997). It stopped only on the known repository-wide Kanon lint baseline. After the additive merge of current main, an apples-to-apples linked-worktree comparison was exact: branch and current main each report 163 violations, 28 suppressed findings, strict equivalent 191, with identical rule distributions. The one non-force `--no-verify` feature-branch push was explicitly authorized from that immutable receipt. Exact-head scoped receipts: - `bash -n` and `shellcheck` on `scripts/check-docs-only-kernel-gate.sh` - topology/malformed negative fixture passes and invokes the canonical inventory checker - `scripts/check-wiring-inventory.sh --no-log`: 123 modules / 30 capabilities / 23 witness markers / 9 compiled-only owners - doc inventory, witness extraction, and kernel build-entrypoint checks pass - Actionlint and TOML parsing pass - `kanon gate --tier=nobuild --force --paths ...`: 0 findings - `git diff --check`: clean No `Gate-Passed` trailer is asserted; hosted full admission remains authoritative. Closes #944. Co-authored-by: admin <admin@ardentleatherworks.com>
🤖 I have created a release *beep* *boop* --- ## [0.9.1](v0.9.0...v0.9.1) (2026-09-03) ### Bug Fixes * **ci:** correct kernel-window remediation ([#945](#945)) ([4680d75](4680d75)), closes [#933](#933) * **ci:** enforce inventory in required kernel gate ([#955](#955)) ([b2c706b](b2c706b)) * **kernel:** contain syscall user-memory faults ([#952](#952)) ([437edaf](437edaf)) * **watchdog:** bound failed shutdowns ([#946](#946)) ([edd574d](edd574d)) --- This PR was generated with [Release Please](https://github.com/googleapis/release-please). See [documentation](https://github.com/googleapis/release-please#release-please). Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: Cody Kickertz <cody.kickertz@pm.me>
Closes #871
Summary
copy_from_user/copy_to_userprimitives that validate the complete overflow-checked range against the live caller VAS and PL0 permissions.LDRBTorSTRBTabort becomesEFAULT; unrelated PL1 faults still halt.EFAULTfor failed futex copyin.Acceptance evidence
The QEMU uaccess witness exercises a live caller page table rather than a host predicate:
PROT_NONE, unmapped, stale-after-munmap, and cross-page partial ranges returnEFAULT;EINVAL) because this MMU cannot express execute-without-data-read, while the pure descriptor tests reject execute-only data access;LDRBTandSTRBTfaults bypass prevalidation and recover only at the registered transfer PCs;kfaultwitness still halts on an unrelated PL1 undefined instruction (runner rc 4).Cross-process and transition coverage includes distinct-L1 address-space isolation, same-VA/different-VAS futex ownership, caller-owned fd/socket fixtures, forked-child copyout, independent exec replacement, fork-then-exec copyin in both old and replacement VASes, and real target
mprotect/munmaptransitions. Pipe publication and read consumption occur only after successful copyout, satisfying #868's no-side-effect failure boundary.Verification
env -u CARGO_TARGET_DIR scripts/kernel-clippy.sh— all nine ARM feature configurations passed.env -u CARGO_TARGET_DIR scripts/kernel-host-tests.sh— 2,571/2,571 default and 2,575/2,575 debug-console passed; one intentional skip in each.scripts/check-target-test-ledger.sh— 129 rows, 29 both-mechanism, 9 target-only, 2,576 runnable host tests, no drift.scripts/check-witness-extraction.sh,cargo fmt --all --manifest-path crates/thumos/Cargo.toml -- --check,git diff --check, and changed-shell syntax — passed.env -u CARGO_TARGET_DIR scripts/witness/uaccess.sh— passed.env -u CARGO_TARGET_DIR scripts/witness/kfault.sh— passed with expected runner rc 4.env -u CARGO_TARGET_DIR scripts/witness-run-all.sh—ALL KERNEL QEMU WITNESSES: PASS, including fork, exec, and forkexec.kanon gate --full --git-diff origin/main --stamp .— workspace check, dependency audit, clippy, and 997/997 nextest passed; only Kanon lint failed.Kanon #756 baseline receipt
A fresh linked worktree at exact base
a6f855bcc54b2083d05b274f4aa2a3654dadb4a6reproduced exactly the branch result: 163 violations, 28 suppressed, strict-equivalent 191, with identical rule counts. This is the known repository lint debt tracked by #756. Per the documented exception, the branch was pushed with--no-verify; noGate-Passedtrailer was fabricated.Truthful residuals
recvfromprevalidates both outputs and preserves the datagram on a payload transfer fault, but a late payload fault can leave the already-copied source-address bytes in user memory; the pair is not byte-atomic./dev/urandompreserves file data/offset behavior, but its volatile PRNG state may advance if a post-validation copyout fault occurs.sendto/recvfrom; direct helpers support address buffers. The existingaddr_lenoutput andexecveenvpremain unimplemented/ignored rather than being newly claimed here.