B5 syscall boundary: error taxonomy (T-020) + EL0→EL1 SVC dispatch (T-021)#34
Conversation
ADR-0030 settles the EL0->EL1 syscall calling convention (x8 = number, x0-x5 args, x0 status + x1-x7 payload, SVC #0), the dedicated-status error encoding, and the K2-5 split of IpcError::InvalidCapability into StaleHandle / WrongObjectKind / MissingRight (with the per-subject-cap security argument and the arena-staleness ordering caveat). ADR-0031 fixes the v1 syscall set (send, recv, console_write [capability-gated + release debug-gated], task_yield, task_exit), reserves number 0 as invalid, and pins each call's register layout; every object-naming syscall performs a capability check (P1/P4). Opens T-020 (error taxonomy + Capability/CapObject Debug redaction — the pure-Rust foundation, In Progress) and T-021 (SVC trap trampoline + panic-free dispatcher + copy-from/to-user — Ready, the security-critical hardware-facing half) to ground both ADRs' dependency chains per ADR-0025 Rule 1. Both ADRs land at Proposed; Accept follows in a separate commit. Refs: ADR-0030, ADR-0031 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…view
Flip ADR-0030 and ADR-0031 Proposed -> Accepted in a commit separate from
the propose draft, per write-adr skill section 10. The careful re-read plus
a same-day maintainer review surfaced and corrected several drafting issues
*before* this Accept — all folded into the proposed bodies above, so the
Accepted text is correct from the start (no post-Accept body edit):
- an SVC from a B5 EL1 kernel-stub takes the current-EL (VBAR_EL1+0x200)
sync vector, not the lower-EL (+0x400) EL0 vector, so the real EL0
round-trip is runtime-verified in B6, not B5;
- console_write is capability-gated on a debug-console capability (it was
ambient authority, a P1/P4 violation); the release debug-gate is a
separate, independent defense-in-depth gate;
- the syscall numbers 1..5 are a fixed decision (tests regression-verify
them), and the payload registers are x1..x7.
Adds the additive ADR-0017 revision rider recording that the IpcError
taxonomy is refined (not superseded) and the three-primitive surface is
unchanged.
Refs: ADR-0030, ADR-0031
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Per ADR-0030's K2-5 bundle, replace the collapsed IpcError::InvalidCapability with StaleHandle / WrongObjectKind / MissingRight so the in-kernel and the future userspace error spaces agree and each failure is a distinct, handleable case. Validation now resolves in the order resolve -> type-check -> authority (kind before rights), matching CapError's InvalidHandle/WrongKind/ InsufficientRights shape, across validate_ep_cap, validate_notif_cap, and sched::resolve_ep_cap; the four arena-staleness sites map to StaleHandle. Revealing which check failed is safe for a per-subject, unforgeable capability table (ADR-0030 security argument). Remaps the existing rights/stale test assertions and adds 5 new tests pinning each variant (incl. wrong-kind-with- right, proving kind-before-rights, and a destroyed-endpoint StaleHandle). InvalidTransferCap is intentionally left intact (note C3-008). Updates docs/architecture/ipc.md taxonomy section. Security-relevant (capabilities + IPC). fmt / host-test (194 kernel) / host-clippy / kernel-clippy / kernel-build / miri (no UB) all green. Refs: ADR-0030 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Per ADR-0030 "Security of the taxonomy split" / B5 sub-item 6 (K3-9, security review section 6): a userspace-reachable log path (the future console_write syscall) must never disclose the kernel object a capability names. Replace the derived Debug on Capability with a hand-written impl that shows rights but prints the object as <redacted>, and redact CapObject likewise (kind-only Debug, hiding the wrapped slot index + generation). The individual kernel- object handle types keep their derived Debug for kernel-internal diagnostics (they never cross to userspace; T-021's console_write review gates that). Two host tests pin both redaction layers. Broadens security-model.md's "no unredacted Debug/Display" rule to capabilities. The CapObject redaction was folded in from an adversarial self-review that flagged it as a latent defense-in-depth gap (no current production formatter, but conservative per CLAUDE.md rule 1). Security-relevant. Refs: ADR-0030 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Add two tests so ipc_cancel_recv pins all three split variants (it already had MissingRight): a Task cap carrying RECV proves the kind-before-rights ordering (WrongObjectKind), and a cap whose endpoint was destroyed exercises the arena-staleness branch (StaleHandle). This makes ADR-0030's row-3 verification mapping accurate for cancel_recv (it previously over-claimed cancel coverage). Kernel host tests 194 -> 196. Refs: ADR-0030 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Address the remaining maintainer-review findings (the ADR append-only fix landed via the propose/accept rebase; this commit covers the rest): - Major: phase-b §B5 acceptance over-promised a real EL0->EL1 round-trip, which ADR-0030 shows is impossible at B5 (an EL1 kernel-stub SVC takes the current-EL 0x200 vector, not the lower-EL 0x400 EL0 vector). Narrow B5 to "dispatch mechanism verified via the current-EL kernel-stub" and move the real EL0 0x400 round-trip to the B6 acceptance criteria. - Minor: current.md banner said "In Progress" while the fields said "In Review"; fix the banner and the two broken T-021 links (../). - Move T-020 to In Review in the task index + task doc; record the maintainer-review round and the row-to-verification mapping (now incl. the two new cancel_recv variant tests) in T-020's review history. - Add EL0/EL1, SVC, Syscall, and Syscall ABI glossary entries and note the taxonomy split on the ipc.md architecture status row. Refs: ADR-0030, ADR-0031 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Second-round review found the four *_wrong_object_kind tests handed the cap the operation's own right, so they returned WrongObjectKind under *both* the chosen kind-first order and a hypothetical rights-first regression — i.e. ordering-agnostic, proving nothing (a rights-first flip would not fail them). Fix: each test now uses a wrong-kind cap that also LACKS the required right (CapRights::empty()), the only input that discriminates the order (WrongObjectKind under kind-first, MissingRight under rights-first), so a regression to rights-first now fails the tests. Updates the section comment and T-020 AC#4 to state what each test actually proves; corrects T-020's stale test counts (AC#6 194 -> 196; review history "8 new" -> "9 new"). (The stale-variant references in the Turkish technical-analysis IPC chapter were also refreshed on disk for local reference, but that tree is gitignored, so it is not part of this commit / the repo.) No production code change; fmt / host-test 196 / clippy / build / miri (no UB) all green. Refs: ADR-0030 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…her, copy-user Land the security-critical hardware-facing half of B5 (T-021): the EL0→EL1 SVC trap path that instantiates ADR-0030's calling convention and ADR-0031's five-syscall v1 set. New architecture-agnostic, panic-free, host-tested kernel `syscall` module: - error.rs: SyscallError composing CapError/IpcError via From, with a stable numeric status encoding (0 = Ok; 1-3 top-level; 0x10x = Cap; 0x20x = Ipc). - abi.rs: SyscallNumber decode (release debug-gate on console_write via cfg!(debug_assertions)), the register frame types, value↔register packing for Message/outcomes, and the Option<CapHandle> null-handle sentinel. - user_access.rs: UserAccessWindow + validated copy_from_user/copy_to_user (range-check-then-copy; wrap and zero-length handled; never derefs an unvalidated user pointer). - dispatch.rs: the panic-free dispatcher + per-syscall handlers + the debug-console capability check; control-plane syscalls (task_yield/exit) return a SyscallEffect directive rather than touching the scheduler. Capability surface: CapObject::DebugConsole (singleton, no handle) + CapRights::CONSOLE_WRITE (bit 7, added to KNOWN_BITS) + CapHandle::from_raw (ABI-decode constructor; reconstructed handles are validated by lookup). BSP (hardware-facing): tyrne_sync_trampoline in vectors.s installed at both VBAR_EL1+0x200 (current-EL, the B5 path) and +0x400 (lower-EL AArch64, the B6 EL0 path) — saves the full x0-x30 + SP_EL0 + ELR_EL1 + SPSR_EL1 frame, routes ESR_EL1.EC==SVC64 to a Rust syscall_entry, else to the existing panic path. SyscallTrapFrame (272 B, #[repr(C)], const-asserted to match the asm). kernel_entry runs an EL1 kernel-stub SVC smoke (console_write + bad-number). Gates: fmt / host-clippy / kernel-clippy / kernel-build clean; host tests 236 (+40); cargo test --release green (the debug-gate release-path tests); cargo miri test --workspace --exclude tyrne-bsp-qemu-virt clean (43+236+53). QEMU smoke (debug): two SVCs taken at the current-EL vector (ESR 0x15/SVC64, EL1→EL1), clean ERET; console_write emits its buffer via the syscall path (status 0x0, 63 bytes); a reserved-invalid number returns BadSyscallNumber (0x1); -d int,unimp,guest_errors shows no new fault class; the cooperative demo still runs to "tyrne: all tasks complete". The real EL0 +0x400 round-trip (EL0↔EL1 transition + copy-user against a separate userspace TTBR0_EL1) is wired but runtime-verified in B6 per ADR-0030 §Simulation. Refs: ADR-0030, ADR-0031 Audit: UNSAFE-2026-0029, UNSAFE-2026-0030 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…le-time payload guard
Apply the actionable findings from the T-021 adversarial review-round (which
confirmed no live B5 defect). All changes are test coverage, a behavior-
preserving defensive refactor, and B6 forward-gate tracking — no production
behavior change (QEMU trace byte-stable; the const-generic emits identical
register values).
Test coverage (+4 dispatch-level tests; host tests 236 -> 240) closing gaps the
review surfaced:
- send_with_transfer_cap_then_recv_returns_cap_in_x6 — the x5 transfer-handle
decode -> ipc_send cap_take AND ipc_recv -> encode_recv_outcome x6 cap-pack,
end-to-end through dispatch (previously untested; verified non-vacuous via a
mutation check — breaking the x6 pack makes it fail).
- send_with_stale_transfer_handle_returns_invalid_transfer_cap — status 0x205.
- recv_with_no_sender_returns_pending_packing — Pending packing (x1=pending,
x2..x7 zeroed).
- console_write_exactly_one_chunk_emits_all_bytes — the len == CONSOLE_WRITE_CHUNK
loop boundary (debug-gated).
Hardening (nit): SyscallReturn::with_payload is now a const-generic
with_payload::<IDX> with `const { assert!(IDX < 7) }`, turning the (already
unreachable-from-untrusted-input) runtime index panic into a compile-time error
at the call site — matching the kernel's compile-time-guard idiom. Call sites
updated to the ::<N> turbofish.
Clarity: the three scattered "unreachable re-validation" comments in
sys_console_write consolidated into one inequality-chain proof.
Docs: phase-b.md §B6 gains an explicit "T-021 carry-forward gates" list (per-task
console_write window + per-page user-VA translation returning FaultAddress not
panic; SP_EL1 init on the +0x400 entry; SYSCALL_STUB_TABLE -> current-task table)
so B6 cannot miss them; T-021 review history records the round.
Gates re-run green: fmt / host-clippy / kernel-clippy / kernel-build clean;
host-test 240; test --release green; miri --workspace excl BSP clean (43+240+53,
0 UB); QEMU smoke round-trip byte-stable.
Refs: ADR-0030, ADR-0031
Audit: UNSAFE-2026-0029, UNSAFE-2026-0030
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Reviewer's GuideOpens the B5 syscall boundary by splitting IPC capability errors into precise variants, redacting capability Debug output, and adding a new panic-free, architecture-agnostic syscall module plus an EL1 SVC trap/dispatch path with a debug-console capability, validated copy-from/to-user, and an SVC sync trampoline wired at both current-EL and lower-EL vectors. Sequence diagram for SVC-based syscall dispatch with console_writesequenceDiagram
actor Caller as EL1_kernel_stub
participant Vec as Sync_vector_+0x200
participant Tramp as tyrne_sync_trampoline
participant Entry as syscall_entry
participant Disp as syscall::dispatch
participant CapTbl as CapabilityTable
participant UA as UserAccessWindow
participant Cons as Console
Caller->>Vec: SVC #0 (x8=ConsoleWrite, x0=cons_cap, x1=ptr, x2=len)
Vec->>Tramp: branch to tyrne_sync_trampoline
Tramp->>Tramp: save x0..x30, SP_EL0, ELR_EL1, SPSR_EL1
Tramp->>Entry: bl syscall_entry(*mut SyscallTrapFrame)
Entry->>Disp: dispatch(SyscallContext, SyscallArgs)
Disp->>CapTbl: validate_debug_console_cap(caller_table, cons_cap)
CapTbl-->>Disp: Ok or CapError
alt Cap error
Disp-->>Entry: SyscallEffect::Resume(error SyscallReturn)
else Capability ok
Disp->>UA: UserAccessWindow::validate(ptr, len)
UA-->>Disp: Ok or SyscallError::FaultAddress
alt FaultAddress
Disp-->>Entry: SyscallEffect::Resume(error SyscallReturn)
else Range ok
loop chunks
Disp->>UA: copy_from_user(user_window, chunk_ptr, buf)
UA-->>Disp: Ok or SyscallError
Disp->>Cons: write_bytes(buf[..chunk])
end
Disp-->>Entry: SyscallEffect::Resume(ok SyscallReturn)
end
end
Entry-->>Tramp: write x0(status), x1..x7(payload) into frame
Tramp->>Tramp: restore registers, SP_EL0, ELR_EL1, SPSR_EL1
Tramp-->>Caller: eret (status & payload in x0..x7)
File-Level Changes
Tips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess your dashboard to:
Getting Help
|
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (2)
🚧 Files skipped from review as they are similar to previous changes (2)
📝 WalkthroughWalkthroughThis PR implements the EL0→EL1 syscall dispatch boundary for the Tyrne kernel. It introduces a panic-free kernel dispatcher with stable error encoding, validated user memory access, and capability-gated console I/O. The IPC capability validation error space is split into three granular variants following a prescribed validation order. The BSP wires an EL1 sync exception trampoline to the dispatcher and exercises the boundary via a kernel-stub smoke test. Two ADRs (0030, 0031) and comprehensive task specifications document the design decisions and acceptance criteria. ChangesSyscall Dispatcher Implementation and Integration
Design, Requirements, and Decision Documentation
Sequence Diagram(s)sequenceDiagram
participant Trampoline
participant SyscallEntry
participant Dispatch
participant IPC
participant Console
Trampoline->>SyscallEntry: SyscallTrapFrame ptr
SyscallEntry->>Dispatch: SyscallContext + SyscallArgs
alt ConsoleWrite
Dispatch->>Console: validate cap / copy_from_user / write chunks
Console-->>Dispatch: byte count / status
else Send/Recv
Dispatch->>IPC: ipc_send / ipc_recv
IPC-->>Dispatch: outcome / status
end
Dispatch-->>SyscallEntry: SyscallEffect (Resume / Reschedule / Terminate)
SyscallEntry->>Trampoline: write back registers / eret
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Comment |
current.md + T-021 task file cite PR #34 (base main, 9 commits, bundles T-020 + T-021 in one combined review per the maintainer's call). Matches the project's PR-reference convention (cf. T-019/PR #31, B4/PR #33). Refs: ADR-0030, ADR-0031 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Code Review
This pull request implements the EL0→EL1 SVC syscall dispatch boundary (Task T-021) and refactors the IPC error taxonomy (Task T-020) in accordance with ADR-0030 and ADR-0031. It introduces a panic-free, architecture-agnostic kernel syscall subsystem that decodes the register ABI, validates capabilities, and performs memory-safe user-space copies. On the BSP side, it implements the assembly-level SVC sync trap trampoline and register frame handling, which are smoke-tested via an EL1 kernel-stub. Additionally, IpcError::InvalidCapability is split into granular StaleHandle, WrongObjectKind, and MissingRight variants, and custom redacting Debug implementations are added to Capability and CapObject to prevent diagnostic leaks of internal handle identities. As there are no review comments, no feedback is provided on them.
There was a problem hiding this comment.
Hey - I've found 1 issue, and left some high level feedback:
- The
NULL_CAP_HANDLEsentinel assumes CapHandle indices/generations are constrained to the low 48 bits; consider adding a compile-time assertion (or a doc comment on CapHandle’s fields) to lock in those bit-width assumptions so future changes can’t accidentally collide with the sentinel. - Syscall tests repeatedly construct the same
SyscallContextscaffolding (endpoint arena, queues, table, fake console, user window); extracting a small helper/builder for this setup would make the tests shorter and reduce the chance of subtle inconsistencies between cases.
Prompt for AI Agents
Please address the comments from this code review:
## Overall Comments
- The `NULL_CAP_HANDLE` sentinel assumes CapHandle indices/generations are constrained to the low 48 bits; consider adding a compile-time assertion (or a doc comment on CapHandle’s fields) to lock in those bit-width assumptions so future changes can’t accidentally collide with the sentinel.
- Syscall tests repeatedly construct the same `SyscallContext` scaffolding (endpoint arena, queues, table, fake console, user window); extracting a small helper/builder for this setup would make the tests shorter and reduce the chance of subtle inconsistencies between cases.
## Individual Comments
### Comment 1
<location path="bsp-qemu-virt/src/syscall.rs" line_range="88-97" />
<code_context>
+/// EL0 task derives a tighter window from its own mapped region (see
+/// [`UserAccessWindow`]'s module docs). The subtraction is a `const` (no runtime
+/// arithmetic): `PMM_EXTENT_END > PMM_EXTENT_START` by construction.
+const SYSCALL_USER_WINDOW_LEN: usize = crate::PMM_EXTENT_END - crate::PMM_EXTENT_START;
+
+/// Rust entry for the `SVC` sync trampoline (`vectors.s`).
</code_context>
<issue_to_address>
**suggestion (bug_risk):** Guard against accidental SYSCALL_USER_WINDOW_LEN underflow at compile time
This relies on `PMM_EXTENT_END > PMM_EXTENT_START`, but if that invariant is ever broken (e.g. misconfigured PMM extent), the `const` subtraction will wrap in release and corrupt the user window length. Please enforce the ordering at compile time, e.g. with a `const` assertion like `const _: () = assert!(crate::PMM_EXTENT_END >= crate::PMM_EXTENT_START);` before computing the difference, or via a build-time check in `build.rs`, so misconfigurations fail the build deterministically.
```suggestion
/// Length of the syscall copy-from/to-user window in B5: the whole
/// identity-mapped RAM extent the bootstrap address space covers.
///
/// The B5 EL1 kernel-stub runs on the bootstrap AS, which identity-maps the
/// managed extent (per [ADR-0027 §Decision outcome (a)]), so the stub's buffer
/// — a `.rodata`-resident `&[u8]` in the kernel image — is in range. B6's real
/// EL0 task derives a tighter window from its own mapped region (see
/// [`UserAccessWindow`]'s module docs). The subtraction is a `const` (no runtime
/// arithmetic): `PMM_EXTENT_END > PMM_EXTENT_START` by construction.
///
/// Guard against misconfigured PMM extents: enforce ordering at compile time so
/// that the subtraction cannot underflow and wrap in release builds.
const _: () = assert!(crate::PMM_EXTENT_END >= crate::PMM_EXTENT_START);
const SYSCALL_USER_WINDOW_LEN: usize = crate::PMM_EXTENT_END - crate::PMM_EXTENT_START;
```
</issue_to_address>Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.
There was a problem hiding this comment.
Actionable comments posted: 3
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@docs/analysis/tasks/phase-b/T-021-syscall-dispatch.md`:
- Line 11: Update the sentence that claims "discharges rows 0/1/2/4/5" so it
explicitly scopes the discharge to the B5 mechanism-side via the proxy (+0x200)
and not the EL0 runtime verification (+0x400); state that EL0 runtime proof is
deferred to Phase B6. Refer to the same ADR identifiers (ADR-0030, ADR-0031),
the row numbers (0/1/2/4/5), the proxy offset `+0x200`, the runtime offset
`+0x400`, and the deferred `task_create_from_image` wrapper to make the
distinction unambiguous.
In `@docs/roadmap/current.md`:
- Around line 7-10: The blockquote sections (the two leading > paragraphs
starting "**2026-05-29 update — T-021..." and "**2026-05-29 update — B5
opened...") contain blank lines between adjacent quoted paragraphs which
violates markdownlint MD028/no-blanks-blockquote; edit docs/roadmap/current.md
to remove the empty lines inside each blockquote so quoted paragraphs are
contiguous (no empty line between lines beginning with >), preserving all text
and line breaks otherwise so the blockquotes remain semantically identical.
In `@kernel/src/syscall/user_access.rs`:
- Around line 114-154: The unsafe block uses core::ptr::copy_nonoverlapping but
UserAccessWindow::validate only proves bounds, not that the user buffer and
kernel slice are disjoint, making the non-overlap precondition unsound; change
the copy to core::ptr::copy (or otherwise prove non-aliasing for the
kernel-owned dst/src) in the routines that perform user/kernel memory moves (the
unsafe block containing core::ptr::copy_nonoverlapping in copy_from_user /
copy_to_user), and update the surrounding SAFETY comment to remove the unproven
“source and destination are disjoint” claim and document that overlapping copies
are handled by using core::ptr::copy (or document the new aliasing-proof).
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 43743d4c-d48f-4c7f-8d5e-d685c2f2daa7
📒 Files selected for processing (29)
bsp-qemu-virt/src/main.rsbsp-qemu-virt/src/syscall.rsbsp-qemu-virt/src/vectors.sdocs/analysis/tasks/phase-b/README.mddocs/analysis/tasks/phase-b/T-020-syscall-error-taxonomy.mddocs/analysis/tasks/phase-b/T-021-syscall-dispatch.mddocs/architecture/README.mddocs/architecture/exceptions.mddocs/architecture/ipc.mddocs/architecture/security-model.mddocs/audits/unsafe-log.mddocs/decisions/0017-ipc-primitive-set.mddocs/decisions/0030-syscall-abi.mddocs/decisions/0031-initial-syscall-set.mddocs/decisions/README.mddocs/glossary.mddocs/roadmap/current.mddocs/roadmap/phases/phase-b.mdkernel/src/cap/mod.rskernel/src/cap/rights.rskernel/src/cap/table.rskernel/src/ipc/mod.rskernel/src/lib.rskernel/src/sched/mod.rskernel/src/syscall/abi.rskernel/src/syscall/dispatch.rskernel/src/syscall/error.rskernel/src/syscall/mod.rskernel/src/syscall/user_access.rs
…guard fixes Address the second review-round on PR #34. Fix only still-valid issues; one finding (release-wrap of a const) was refuted but its requested guard kept as a clearer compile-time tripwire; one (test-scaffolding helper) skipped. Soundness (headline): copy_from_user / copy_to_user are SAFE `pub fn`s, so they must be sound for every input — but `UserAccessWindow::validate` proves *bounds*, not *disjointness*, and under the v1 identity map (VA == PA) a caller could pass a user_ptr range that aliases the kernel-owned dst/src slice, making `copy_nonoverlapping`'s non-overlap precondition violable from safe code (UB). Switch both moves to `core::ptr::copy` (memmove), which is correct for any overlap; drop the unprovable "source and destination are disjoint" claim from the SAFETY comments and document why `copy` (not `copy_nonoverlapping`) is the sound choice. Behaviour is identical for the non-overlapping case (all current callers are disjoint), so QEMU/Miri/host evidence is unchanged. UNSAFE-2026-0030 gains an append-only Amendment recording the change (title/anchor preserved). Hardening (compile-time guards, no runtime cost): - abi.rs: a `const _: () = assert!(NULL_CAP_HANDLE > max-packable-handle-word)` locks the sentinel-collision-freedom invariant — a future CapHandle widening that could push a packed word into the sentinel's bit range fails the build. - bsp syscall.rs: an explicit `const _: () = assert!(PMM_EXTENT_END >= PMM_EXTENT_START)` with a clear message in front of SYSCALL_USER_WINDOW_LEN. (The reviewer's "wraps in release" premise is incorrect — the subtraction is a `const`, and const-eval rejects underflow at build time, never wraps — but the named assert gives a clearer failure than a raw const-eval overflow error.) Docs: - T-021 §Informs: scope the ADR-0030 §Simulation discharge — rows 2/4 in full + the mechanism half of rows 0/1/5 via the EL1-stub proxy at the current-EL +0x200 vector; the EL0-runtime half of rows 0/1/5 (the +0x400 vector, the EL0↔EL1 transition, copy-user vs a separate userspace TTBR0_EL1) is deferred to B6, not discharged here. - current.md: remove the blank lines between adjacent banner paragraphs so they form one contiguous `>` blockquote (matches the file's existing multi-paragraph style; resolves markdownlint MD028). Skipped: extracting a SyscallContext test-builder — the scaffolding verbosity is largely forced by the borrow structure (tests declare + later inspect the borrowed locals), so a helper saves only the struct-literal line per test and isn't worth churning the just-reviewed test suite on an in-review PR. Gates re-run green: fmt / host-clippy / kernel-clippy / kernel-build; host-test 240; test --release 233; miri --workspace excl BSP clean (0 UB); QEMU smoke round-trip byte-stable. Refs: ADR-0030, ADR-0031 Audit: UNSAFE-2026-0030 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (1)
kernel/src/syscall/user_access.rs (1)
162-162: ⚡ Quick winAdd an overlap regression test for both directions.
The reason this switched to
core::ptr::copyis that the safe API must stay sound even when the user range aliases the kernel slice. Please pin that with one overlappingcopy_from_usercase and one overlappingcopy_to_usercase; otherwise a future cleanup back tocopy_nonoverlappingcan reintroduce UB without a failing test.🧪 Example test shape
#[test] fn copy_from_user_allows_overlap() { let mut backing: Vec<u8> = (0..8u8).collect(); let base = backing.as_mut_ptr() as usize; let window = UserAccessWindow::new(base, backing.len()); copy_from_user(&window, base, &mut backing[2..6]).unwrap(); assert_eq!(&backing[..], &[0, 1, 0, 1, 2, 3, 6, 7]); } #[test] fn copy_to_user_allows_overlap() { let mut backing: Vec<u8> = (0..8u8).collect(); let base = backing.as_mut_ptr() as usize; let window = UserAccessWindow::new(base, backing.len()); let src = &backing[2..6]; copy_to_user(&window, base + 1, src).unwrap(); assert_eq!(&backing[..], &[0, 2, 3, 4, 5, 5, 6, 7]); }Also applies to: 207-207
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@kernel/src/syscall/user_access.rs` at line 162, Add two regression tests ensuring overlapping copies are allowed: one for copy_from_user and one for copy_to_user. Implement a test that creates a backing Vec<u8>, builds a UserAccessWindow from its pointer/length, then calls copy_from_user with a destination slice that overlaps the source range and asserts the resulting backing bytes match the expected overlapping-copy outcome, and likewise a second test that calls copy_to_user with a source slice that overlaps the destination range and asserts the expected bytes. Place tests near existing user_access tests so future refactors of core::ptr::copy vs copy_nonoverlapping on functions copy_from_user and copy_to_user will fail if UB is reintroduced. Ensure both tests exercise opposite overlap directions (dst overlaps src and src overlaps dst).
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@docs/audits/unsafe-log.md`:
- Line 670: Update the amendment header to include the actual commit SHA and
make the text self-consistent: record the commit SHA historically at the top of
the amendment, explicitly mark the original `copy_nonoverlapping` wording and
the "rejected alternatives" reference as historical/superseded, and adjust the
invariant (3) and SAFETY comments to reflect the switch from
`copy_nonoverlapping` to `core::ptr::copy`; ensure the header follows the
audit-log format and clearly states that the named alternative
(`copy_nonoverlapping`) is superseded rather than unchanged.
---
Nitpick comments:
In `@kernel/src/syscall/user_access.rs`:
- Line 162: Add two regression tests ensuring overlapping copies are allowed:
one for copy_from_user and one for copy_to_user. Implement a test that creates a
backing Vec<u8>, builds a UserAccessWindow from its pointer/length, then calls
copy_from_user with a destination slice that overlaps the source range and
asserts the resulting backing bytes match the expected overlapping-copy outcome,
and likewise a second test that calls copy_to_user with a source slice that
overlaps the destination range and asserts the expected bytes. Place tests near
existing user_access tests so future refactors of core::ptr::copy vs
copy_nonoverlapping on functions copy_from_user and copy_to_user will fail if UB
is reintroduced. Ensure both tests exercise opposite overlap directions (dst
overlaps src and src overlaps dst).
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 737ff6aa-bacc-4702-b1de-7fb59d2e7c6e
📒 Files selected for processing (6)
bsp-qemu-virt/src/syscall.rsdocs/analysis/tasks/phase-b/T-021-syscall-dispatch.mddocs/audits/unsafe-log.mddocs/roadmap/current.mdkernel/src/syscall/abi.rskernel/src/syscall/user_access.rs
✅ Files skipped from review due to trivial changes (1)
- docs/analysis/tasks/phase-b/T-021-syscall-dispatch.md
🚧 Files skipped from review as they are similar to previous changes (3)
- docs/roadmap/current.md
- bsp-qemu-virt/src/syscall.rs
- kernel/src/syscall/abi.rs
|
Responding to the two high-level items in the @sourcery-ai review (they have no inline anchor, so noting the disposition here): 1. const _: () = assert!(
NULL_CAP_HANDLE > (((u32::MAX as u64) << 16) | (u16::MAX as u64)),
"NULL_CAP_HANDLE must exceed every packable CapHandle word (see encode_cap_handle)"
);A live handle packs 2. Extract a All re-validated after the follow-up: |
… the soundness basis Review-round on PR #34 (two findings; each verified against current code): 1. UNSAFE-2026-0030 amendment (docs/audits/unsafe-log.md) — VALID, fixed. The amendment added in 2c713c0 (a) lacked the commit SHA the audit-log format wants and (b) over-claimed that switching to `core::ptr::copy` makes the copy "overlap-tolerant". An empirical Miri probe disproved that: an overlapping (user_ptr, kernel-slice) pair is UB *regardless* of the copy primitive — `copy_from_user`'s `dst: &mut [u8]` (and `copy_to_user`'s `src: &[u8]`) parameter is exclusive / shared, so an aliasing access through the exposed `user_ptr` violates that borrow (Stacked Borrows: "not granting access to tag <wildcard> … strongly protected"). The amendment now carries SHA 2c713c0, marks the original `copy_nonoverlapping` §Operation / invariant(3) / rejected-alternatives wording as superseded, and states the true soundness basis: the user/kernel **disjointness** invariant (user_ptr = userspace, kernel slice = distinct allocation in v1 / separate AS in B6), under which both `copy` and `copy_nonoverlapping` are sound. `core::ptr::copy` is kept as the conservative primitive. The copy_from_user / copy_to_user SAFETY comments are corrected to match (invariant 3 = disjointness, not "overlap-tolerant"). 2. Add overlapping-copy regression tests — SKIPPED, with reason. The requested tests assert "overlapping copies are allowed", but overlap is UB here (see above — Miri-confirmed via a temporary probe, now removed), independent of `copy` vs `copy_nonoverlapping`. Such tests would (a) break the Miri gate and (b) codify an unsound expectation. The real invariant is disjointness, which the existing tests + the structural user/kernel split already cover; an overlapping call correctly fails under Miri's borrow model. No code-behaviour change (the `core::ptr::copy` calls are unchanged; only SAFETY comments + the audit amendment text). Gates: fmt / host-clippy / kernel-clippy / kernel-build clean; host-test 240; miri (syscall) 0 UB. Production code is byte-identical to 2c713c0, already validated with full miri + test --release + QEMU smoke. Refs: ADR-0030, ADR-0031 Audit: UNSAFE-2026-0030 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
B5 closure performance baseline — the performance leg of the B5 syscall-boundary closure trio. Re-baseline of kernel footprint + boot-to-end timing after T-020 + T-021 (PR #34, f98e1af) versus the 2026-05-28 B4 closure baseline. Footprint (release): .text 34,648 (+1,524) / .rodata 4,856 (+296) / .bss 50,592 (+2,272) = ~88.0 KiB (+4.76 %) — the smallest non-refactor .text growth in Phase B (the syscall boundary is a thin validator/dispatch layer). Tests 339 (43 hal + 240 kernel + 53 test-hal + 3 doc; +53 kernel), miri clean (0 UB, run locally). Release harness band p10/p50/p90 = 17.645 / 20.300 / 24.706 ms. Verdict: baseline, no proposal. The cycle's decisive measurement was a same-host back-to-back control (the B4 binary 3ab029f rebuilt + re-measured this session): it proves the ~+2.9 ms p10/p50 delta vs B4 is REAL B5 code (the boot SVC-smoke — 2 exception round-trips + cold TCG translation), not host jitter, correcting an initial mis-read of the noisier raw band. One-time-at-boot, ~us on real hardware. The control also shows the QEMU-TCG harness is nearing its resolving floor for small milestones (per-milestone signal ~ session jitter). Adds the harness report docs/analysis/reports/perf-baseline-2026-05-29-B5-closure.md and the README index row. Refs: ADR-0013, ADR-0030, ADR-0031 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
B5 closure retrospective — the business leg of the B5 syscall-boundary closure trio — plus the roadmap forward motion it drives. Formally closes Milestone B5 (Syscall boundary): T-020 (IpcError taxonomy split + Capability/CapObject Debug redaction) + T-021 (EL0 to EL1 SVC dispatch) merged via PR #34 (f98e1af); ADR-0030 + ADR-0031 Accepted. What landed (canonical metrics, reproduced live at afeed10): 339 host tests (+53 kernel) incl. local miri 0 UB; release ELF ~88.0 KiB (+4.76 % vs B4); release smoke clean to "all tasks complete" (the release trace itself proves the console_write debug-gate — status=0x1 in release); the debug smoke shows the full console_write SVC round-trip (status=0x0, bytes=63); -d = 712/776 PL011 + 2 expected SVC exceptions (EL1 to EL1, ESR 0x15, clean ERET), zero new fault class; audit log 30 entries (29 Active) — UNSAFE-2026-0029/0030. What we learned: the pure-Rust (T-020) / hardware-boundary (T-021) split per CLAUDE.md section 6 let the most security-sensitive milestone land safely in one calendar day without skipping rigor; an adversarial pass + Miri corrected a real copy-user soundness over-claim (disjointness, not the copy primitive, is the basis); a same-host perf control corrected a host-jitter mis-attribution; ABI front-loading kept the syscall numbers a decision, not an accident. Side-effects: current.md banner + Pathfinder flipped to B5-closed / B6-next; phase-b.md section B5 status -> Closed; test-count drift (236/240 mid-arc -> live 339) reconciled; business-reviews README index row added. Next: B6 (first userspace "hello") — the deferred task_create_from_image bridge + the 3 T-021 carry-forward gates + the ADR-0033 high-half opening; B6's review is the Phase B retrospective. Refs: ADR-0013, ADR-0030, ADR-0031, ADR-0017, ADR-0033 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…estone B5) (#35) * docs(analysis): security-review B5-syscall-boundary Standalone consolidated security pass over the B5 syscall boundary (T-020 IpcError split + Capability/CapObject Debug redaction K3-9; T-021 EL0→EL1 SVC dispatch), merged via PR #34 (f98e1af). Performed with a fresh 8-axis checklist after the PR's three code-review rounds + the adversarial multi-agent pass. Verdict: **Approve.** All eight applicable axes pass (cryptography N/A). The dispatcher is panic-free on every register-supplied input; every object-naming syscall is capability-gated before any effect (console_write on the new debug-console cap, closing the ambient-authority slip caught before ADR Accept); copy-from/to-user never dereferences an unvalidated user pointer, with the soundness basis honestly recorded as the user/kernel disjointness invariant (an empirical Miri probe disproved an earlier "overlap-tolerant" over-claim — overlap is UB regardless of the copy primitive via the &mut/& borrow exclusivity); T-020's Debug redaction closes the K3-9 secrets-leak path exactly where B5 first creates a userspace-reachable log channel. Both new unsafe entries (UNSAFE-2026-0029 trampoline asm, UNSAFE-2026-0030 copy-user) are policy-conformant; 0029 carries the required second-reviewer sign-off. Gates: host-test 240, test --release green, miri --workspace clean (0 UB), QEMU +0x200 proxy round-trip byte-stable with no new fault class. B5 builds the boundary mechanism but the real EL0 +0x400 transition is B6; three B6 carry-forward gates (per-task console_write window + per-page translation; SP_EL1 init; SYSCALL_STUB_TABLE → current-task table) are tracked in phase-b.md §B6. This pass can serve as the security leg of the eventual B5 closure trio (business + performance legs deferred — "security review first"). Refs: ADR-0013, ADR-0030, ADR-0031, ADR-0014 Security-Review: @cemililik (+ Claude Opus 4.8 agent) Audit: UNSAFE-2026-0029, UNSAFE-2026-0030 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs(analysis): reconcile security-model.md SMMUv3-CI claim with ADR-0036 (B5 review finding) The B5 syscall-boundary security review (this PR) re-surfaced the one finding actionable outside a phase gate: security-model.md §threat-model #7 + §Open questions still described QEMU `virt` as "launched with SMMUv3 and used in CI to catch driver misbehaviour" — a live contradiction with Accepted ADR-0036 (QEMU `virt` is GICv2 / no-IOMMU in v1; SMMUv3 is exposed only under an explicit `iommu=smmuv3` launch Tyrne does not use). It was N/A as a v1 *defect* (no bus-master driver exists, so the DMA boundary is inactive) but a stale doc claim, forward-flagged since the 2026-05-28 B4 closure. Reconciled conservatively: both sentences now state the v1 GICv2/no-IOMMU reality, point at ADR-0036, and reframe the SMMU-in-CI gate as a future IOMMU-equipped-target (Jetson Orin) item — preserving the model's IOMMU intent, correcting only the stale QEMU-`virt`-has-SMMUv3 claim. The review doc's §8 + forward-flags are updated to mark this RECONCILED (no longer carried forward). All other review forward-flags are correctly phase-deferred (B6 carry-forward gates / Phase-E fault containment / preemption-time ipc_send hardening / B5+ cap_map rights ADR) — no action now. Refs: ADR-0013, ADR-0036 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs(analysis): performance-review B5-closure B5 closure performance baseline — the performance leg of the B5 syscall-boundary closure trio. Re-baseline of kernel footprint + boot-to-end timing after T-020 + T-021 (PR #34, f98e1af) versus the 2026-05-28 B4 closure baseline. Footprint (release): .text 34,648 (+1,524) / .rodata 4,856 (+296) / .bss 50,592 (+2,272) = ~88.0 KiB (+4.76 %) — the smallest non-refactor .text growth in Phase B (the syscall boundary is a thin validator/dispatch layer). Tests 339 (43 hal + 240 kernel + 53 test-hal + 3 doc; +53 kernel), miri clean (0 UB, run locally). Release harness band p10/p50/p90 = 17.645 / 20.300 / 24.706 ms. Verdict: baseline, no proposal. The cycle's decisive measurement was a same-host back-to-back control (the B4 binary 3ab029f rebuilt + re-measured this session): it proves the ~+2.9 ms p10/p50 delta vs B4 is REAL B5 code (the boot SVC-smoke — 2 exception round-trips + cold TCG translation), not host jitter, correcting an initial mis-read of the noisier raw band. One-time-at-boot, ~us on real hardware. The control also shows the QEMU-TCG harness is nearing its resolving floor for small milestones (per-milestone signal ~ session jitter). Adds the harness report docs/analysis/reports/perf-baseline-2026-05-29-B5-closure.md and the README index row. Refs: ADR-0013, ADR-0030, ADR-0031 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs(roadmap): business-review B5-closure B5 closure retrospective — the business leg of the B5 syscall-boundary closure trio — plus the roadmap forward motion it drives. Formally closes Milestone B5 (Syscall boundary): T-020 (IpcError taxonomy split + Capability/CapObject Debug redaction) + T-021 (EL0 to EL1 SVC dispatch) merged via PR #34 (f98e1af); ADR-0030 + ADR-0031 Accepted. What landed (canonical metrics, reproduced live at afeed10): 339 host tests (+53 kernel) incl. local miri 0 UB; release ELF ~88.0 KiB (+4.76 % vs B4); release smoke clean to "all tasks complete" (the release trace itself proves the console_write debug-gate — status=0x1 in release); the debug smoke shows the full console_write SVC round-trip (status=0x0, bytes=63); -d = 712/776 PL011 + 2 expected SVC exceptions (EL1 to EL1, ESR 0x15, clean ERET), zero new fault class; audit log 30 entries (29 Active) — UNSAFE-2026-0029/0030. What we learned: the pure-Rust (T-020) / hardware-boundary (T-021) split per CLAUDE.md section 6 let the most security-sensitive milestone land safely in one calendar day without skipping rigor; an adversarial pass + Miri corrected a real copy-user soundness over-claim (disjointness, not the copy primitive, is the basis); a same-host perf control corrected a host-jitter mis-attribution; ABI front-loading kept the syscall numbers a decision, not an accident. Side-effects: current.md banner + Pathfinder flipped to B5-closed / B6-next; phase-b.md section B5 status -> Closed; test-count drift (236/240 mid-arc -> live 339) reconciled; business-reviews README index row added. Next: B6 (first userspace "hello") — the deferred task_create_from_image bridge + the 3 T-021 carry-forward gates + the ADR-0033 high-half opening; B6's review is the Phase B retrospective. Refs: ADR-0013, ADR-0030, ADR-0031, ADR-0017, ADR-0033 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs(roadmap): pre-B6 prep — reconcile B5-done status + B6 opening sequence Pre-B6 documentation preparation on the B5-closure branch (rides in PR #35), done before B6 development starts on its own branch off main. Docs-only. (1) B5-done status reconcile (drift sweep). With B5 closed, several live docs still treated the syscall boundary as future/next: - CLAUDE.md: "the syscall ABI and first userspace task are next" -> the syscall boundary is done; the first EL0 task is next. - README.md status table: "Syscall ABI + EL0 entry | Next - Phase B5" -> split into "Syscall ABI + dispatcher = Done (B5)" + "First userspace hello (EL0) = Next (B6)". - phase-b ADR ledger: ADR-0030 / ADR-0031 marked Accepted 2026-05-29; ADR-0033 / ADR-0034 triggers corrected from B5/B5+ to B6 (B5 closed via the SVC proxy without surfacing the per-task TTBR0 swap, so the trigger is now B6). - architecture docs (task-loader / memory-management / boot): "ADR-0033 gated on B5 surfacing per-task TTBR0 swap" -> B6, and "until B5 adds a per-task context surface" -> B6 — these would have misled a B6 dev about which milestone owns the EL0 context + high-half work. (2) B6 opening sequence & prerequisites — the careful B6 plan, made durable in phase-b.md section B6. States the gating prerequisite (the kernel must stay reachable from every task's active translation, else an EL0 SVC vector fetch translation-faults), the ADRs that open B6 (ADR-0033 kernel-in-every-AS + the EL0-task-context decision + optional ADR-0034), and the dependency-ordered task sequence: ADR-0033 impl -> EL0 context -> task_create_from_image -> the 3 T-021 carry-forward gates -> tyrne-user + userland/hello -> wire-up + EL0 round-trip smoke -> Phase B retrospective. Decisions remain open for their ADRs; this fixes only the order + rationale. All links resolve; docs-only, no kernel changes. Refs: ADR-0013, ADR-0027, ADR-0030, ADR-0031, ADR-0033 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs: PR #35 review-round — fix assert quote, ADR-0033 link, ADR-0034 table pipe Three review findings on the B5 closure docs, each verified against the source: - abi.rs assert quote (security review section 1): the doc quoted assert!(NULL_CAP_HANDLE > ((u32::MAX << 16) | u16::MAX)) but the actual abi.rs:42 uses `as u64` casts — (((u32::MAX as u64) << 16) | (u16::MAX as u64)) (without them the shift overflows in u32 arithmetic). Quote corrected to match. - ADR-0033 link (security review section 8): the label said "ADR-0033" but the link targeted 0027-kernel-virtual-memory-layout.md (a mismatch). ADR-0033 (high-half migration) is genuinely the right concept — 0027 only reserves the slot — so rather than relabel to ADR-0027 (which would misattribute high-half to 0027), ADR-0033 is now plain text (matching the unlinked ADR-0034 in the same sentence) with a correctly-labeled "reserved in ADR-0027" cross-reference. - ADR-0034 ledger row (phase-b.md): the inline code USER|EXECUTE (introduced in the pre-B6 prep commit) had an unescaped pipe, splitting the table row into five columns; escaped to USER\|EXECUTE. All 14 ledger rows now have 4 columns. Validated: assert quote matches abi.rs:42 verbatim; all links resolve; every ADR-ledger row is well-formed (5 delimiters = 4 columns). Docs-only. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Opens the B5 syscall boundary. This PR bundles two tasks per the maintainer's request — they are reviewable as two distinct commit groups (T-020 is the pure-Rust error-taxonomy foundation; T-021 is the security-critical hardware-facing trap/dispatch path). ADRs 0030 (syscall ABI + K2-5 taxonomy split) and 0031 (five-syscall v1 set) are Accepted and instantiated here.
Commit map
476710b93d5960d20e6d0IpcError::InvalidCapability→StaleHandle/WrongObjectKind/MissingRight324457aCapability/CapObjectDebug(K3-9)4777f9aStaleHandle+WrongObjectKindonipc_cancel_recv1df1b527b35ed6806c9665145d4dT-020 — syscall error taxonomy (the pure-Rust foundation)
IpcError::InvalidCapabilityinto three handleable variants (StaleHandle/WrongObjectKind/MissingRight), validation order resolve → kind → rights, acrossipc/mod.rs+sched/mod.rs+ tests.CapabilityandCapObjectDebugimpls so a userspace-reachable log path cannot leak a kernel object's slot index / generation.T-021 — EL0→EL1 SVC dispatch (the security-critical half)
New architecture-agnostic, panic-free, host-tested kernel
syscallmodule:error.rs—SyscallErrorcomposingCapError/IpcErrorviaFrom, with a stable numeric status encoding (0=Ok,1-3top-level,0x10xCap,0x20xIpc; exhaustive-without-wildcard so a new variant breaks the build).abi.rs—SyscallNumber::decode(the v1 set; number0reserved-invalid; the release debug-gate onconsole_writeviacfg!(debug_assertions)), the register frames, and value↔register packing (Message, outcomes,Option<CapHandle>with theu64::MAXnull sentinel).user_access.rs—UserAccessWindow+ validatedcopy_from_user/copy_to_user(range-check-then-copy; wrap + zero-length handled; never derefs an unvalidated user pointer).dispatch.rs— the panic-free dispatcher + per-syscall handlers + the debug-console capability check; control-plane syscalls (task_yield/task_exit) return aSyscallEffectdirective rather than touching the scheduler.Capability surface:
CapObject::DebugConsole(singleton, no handle) +CapRights::CONSOLE_WRITE(bit 7) +CapHandle::from_raw(ABI-decode constructor; reconstructed handles are validated bylookup).BSP (hardware-facing):
tyrne_sync_trampoline(vectors.s) installed at bothVBAR_EL1+0x200(current-EL, the B5 path) and+0x400(lower-EL AArch64, the B6 EL0 path) — saves the fullx0–x30+SP_EL0+ELR_EL1+SPSR_EL1frame, routesESR_EL1.EC == SVC64to a Rustsyscall_entry, else to the existing panic path.SyscallTrapFrame(272 B,#[repr(C)], const-asserted).kernel_entryruns an EL1 kernel-stub SVC smoke.Security properties verified
An adversarial multi-agent review-round + first-hand tracing confirmed no input a B5 caller can supply crashes the kernel, bypasses a capability check, or dereferences an unvalidated pointer:
x8/x0–x5/a forged handle/a huge-or-zerolen/a wrapping pointer terminates in a typedSyscallErrorvalue.console_writevalidates the debug-console cap (resolve → kind →CONSOLE_WRITE) before any range check or output;send/recvgate insideipc_send/ipc_recv.copy_*validate[ptr, ptr+len)against the active-AS window (half-open bounds, wrap viachecked_add, zero-length short-circuit) before the singlecopy_nonoverlapping.Gates (all green, first-hand)
cargo fmt --check·cargo host-clippy -D warnings·cargo kernel-clippy -D warnings·cargo kernel-build· host tests 240 (43 hal + 240 kernel + 53 test-hal) ·cargo test --release(release debug-gate path) ·cargo miri test --workspace --exclude tyrne-bsp-qemu-virtclean (0 UB) · mutation-check (the new transfer-cap test is non-vacuous).QEMU smoke (debug): the EL1 kernel-stub issues two
SVC #0s —console_writeemits its buffer via the syscall path (status0x0, 63 bytes) + a reserved-invalid number returnsBadSyscallNumber(0x1).-d int,unimp,guest_errorsshows exactly twoSVCexceptions at the current-EL vector (ESR 0x15/0x56000000= SVC64,EL1→EL1), cleanERET, no new fault class; the cooperative demo still runs totyrne: all tasks complete.Audit entries
syscall_entryframe access.Scope / deferred (B6)
The real EL0
+0x400round-trip (EL0↔EL1 transition + copy-user against a separate userspaceTTBR0_EL1) is wired but runtime-verified in B6 per ADR-0030 §Simulation. The review-round's three B6 carry-forward gates are tracked inphase-b.md §B6("T-021 carry-forward gates"): per-taskconsole_writewindow + per-page user-VA translation (returnFaultAddress, never panic),SP_EL1init on the+0x400entry, and theSYSCALL_STUB_TABLE→ current-task-table swap.Security-relevant (the EL0→EL1 trust boundary) — flagged for explicit security review per CLAUDE.md §non-negotiable #1.
Refs: ADR-0030, ADR-0031
Audit: UNSAFE-2026-0029, UNSAFE-2026-0030
🤖 Generated with Claude Code
Summary by Sourcery
Implement the kernel-side syscall boundary with a panic-free SVC-based dispatch path, refine IPC error taxonomy and capability diagnostics for userspace, and extend the BSP to route SVC traps through a new full-register trampoline and syscall entry, with comprehensive tests and documentation.
New Features:
Bug Fixes:
Enhancements:
Documentation:
Tests:
Summary by CodeRabbit