Skip to content

Beta gap closure, Tier 2: governed session cleanup, mock-DNS test, fuzzing, final-scope docs - #4

Open
slika25 wants to merge 5 commits into
mainfrom
beta-tier2
Open

slika25 wants to merge 5 commits into
mainfrom
beta-tier2

Conversation

@slika25

@slika25 slika25 commented Oct 19, 2026

Copy link
Copy Markdown
Contributor

Summary

Tier 2 of the Beta gap closure, on top of #3 (already merged). Five commits: one kernel-arm change, one x86 test, one fuzz addition and two docs commits. No change to which actions MARSHAL gates and no change to any trust boundary.

  • Governed session cleanup (ADR 0010). Data sessions outlived the account suspension, profile deletion or operator disable that made them meaningless, and enough of them filled the 16-row table (THREAT_MODEL gap 17). MVNO suspend, SIM delete and the operator SYS_SIM_DISABLE success path now close them, with no lock held (DATA stays the leaf lock). Each closed row gets one DataActivity audit entry, inside ADR 0009's quotas, and a serial line. This is the already-gated caller narrowing derived state; the policy engine still does not act on its advice.
  • Usage-feed standing and binding check (gap 21). SYS_DATA_ACCOUNT now refuses a non-Active account (code 8) and an Active account that owns no bound profile (code 9), with a Rejected audit and no usage row. The kernel issues data:usage:1 so demo account 1 can exercise it. The usage bytes are still caller-asserted (gap 13).
  • Deterministic mock-DNS test. NetBootInfo gains dns_server: [u8; 4] at offset 124 inside existing tail padding (size stays 128; all-zero means the default 8.8.8.8, so every other net test is unchanged). net_driver_dns_mock points the guest at the QEMU gateway 10.0.2.2, which slirp maps to host loopback, and asserts it resolves exactly 192.0.2.53 from kernel/tests/support/mock_dns.py.
  • Fuzzing. A new boot_sector_fuzz target (BootSectorInfo, FSInfo round trip, slot finding, short names), and .github/workflows/fuzz.yml, triggered only by workflow_dispatch and a weekly schedule, never as a PR gate.
  • Docs. BETA_CRITERIA: M3 moves to MET (as scoped), so the header reads 4 MET / 4 PARTIAL. M1, D3 and D4 keep their statuses and gain final-scope wording (no provisioning or slot reclamation in Beta, permanent blk quarantine until the driver restarts, create/delete/grow stay driver-internal, spawn_instance has no production caller). ROADMAP gains a "Post-v1.0 (not scheduled)" section. STATUS, CHANGELOG, the ADR index and the release notes draft are updated.

What this does not claim

  • The policy engine stays advisory (M3-c); acting on an ActionRequest needs measured, authenticated usage and a kernel operator identity.
  • Usage bytes are unauthenticated; the reconciler runs only when called; the Rejected audit class (quota 48) is shared, so a feeder of refused usage calls can push later genuine rejections into coalesced summaries (evidence and counts are kept).
  • The mock-DNS test no longer proves real resolver reachability; net_driver_dns (real egress) stays local-only.
  • The M3 promotion to MET (as scoped) rests on evidence that has not been observed in a CI run until this PR runs.

Verification, and what CI will show for the first time

Done locally: 161 kernel-arm lib tests; mutation checks on the new session helpers; an independent verifier's review (lock order, bounded audit growth, pinned codes, CI patterns against the real format strings); a secure-config boot on a scratch build passing all 547 extracted assertion lines; the mock-DNS test passing with the mock up and failing as expected with it down; boot_sector_fuzz run for 53,183 execs under libFuzzer with ASan in WSL (no finding).

Never run before this PR (CI triggers only on PRs into main and pushes to main):

  • the new kernel-arm assertions in the mock-EXECUTE step (cleanup count 2, codes 8 and 9; reasoned from the code, not run locally because they need the proxy bridge) and the secure=off plain boot;
  • the mock-DNS step on ubuntu-latest: sudo for port 53, 127.0.0.1:53 being free, and slirp's gateway mapping on libslirp 4.7 are all unverified on Linux;
  • fuzz.yml itself, which cannot be dispatched until it exists on main, and ASan on ubuntu-latest.

A red mock-DNS step would most likely be a Linux environment assumption, not a logic bug.

Known fragility (not introduced here)

The demo capability tokens last 60 s; on a slow or loaded host the boot walk (about 75 s on the Windows machine used here) can outlast them and fail with "capability token expired". CI takes seconds, so it is unaffected. Verification locally used a scratch build with the lifetime raised.

Not in this PR

docs/BETA_MOBILE_PROGRESS.md and docs/THREAT_MODEL.md are local-only. ADR 0011 (a built-in VPN design) is unrelated and is not part of this PR.

…mment

ROADMAP gains a "Post-v1.0 (not scheduled)" section: real hardware support,
an installer/ISO/bootable USB, an Android-phone port and any media pipeline
are outside Alpha through v1.0, so a v1.0 that meets the table is not an
installable operating system. It also records the constraint that RC and
v1.0 rows needing an out-of-image trust root (attestation, WORM boot chain,
secure update channel) need at least a minimal real-hardware bring-up.

STATUS.md: the title and opening paragraph still said "currently in Alpha"
and "cooperative round-robin context switching"; Alpha is complete, Beta is
in progress, and the x86 scheduler is timer-preempted.

net-driver-host-arm syscall.rs: the doc comment claimed el1_vector_common
does not save x5-x8; it does (stp x5, x6 / stp x7, x8 on entry), so
declaring them clobbered is conservative, not required. Comment only.
…the usage feed

THREAT_MODEL gap 17: data session rows outlived the account suspension,
profile deletion or operator disable that made them meaningless, and enough
of them filled the 16-row session table. The governed paths now close them:
MVNO suspend (after the forced disable), SIM delete (after the unbind) and
the operator SYS_SIM_DISABLE success branch call
data::close_sessions_for_account / close_sessions_for_profile with no lock
held (DATA stays the leaf lock). Each closed row gets one DataActivity audit
entry, inside ADR 0009's quotas, and a serial line "data session account A
slot S profile P closed by kernel cleanup after <cause>". This is the
already capability- and MARSHAL-gated caller narrowing derived state, not the
policy engine acting on its advice.

Gap 21: SYS_DATA_ACCOUNT metered Suspended and Closed accounts and accounts
that own no bound profile. It now refuses a non-Active account
(USAGE_NOT_ACTIVE = 8) and an Active account with no bound profile
(USAGE_NO_PROFILE = 9) via data_codes::usage_feed_refusal and the new
mvno::has_bound_profile, with a Rejected-class audit and no usage row. The
kernel issues data:usage:1 so demo account 1 can exercise the refusal.

The data policy engine stays advisory (ADR 0010): nothing performs an
ActionRequest. The usage bytes are still caller-asserted (gap 13).

The boot walk changes accordingly: two SYS_DATA_ACCOUNT calls after the
suspend (codes 8 and 9), the walk's own SESSION_CLOSE now fails with
no-such-open-session, and the post-suspend reconcile is the first clean pass.
Plain-boot CI assertions are updated, and the mock-EXECUTE step gains
assertions for the cleanup lines (not run locally: they need the proxy bridge).

Verified: 161 kernel-arm lib tests; a secure-config boot on a scratch build
with the demo token lifetime raised passes all 547 extracted assertion lines.
net_driver_dns queries 8.8.8.8 through QEMU's NAT and needs real internet
egress, which is why it was dropped from CI (ce9a203). QEMU's slirp maps the
virtual gateway 10.0.2.2 to host loopback, so a guest UDP datagram to
10.0.2.2:53 reaches a host socket on 127.0.0.1:53 with no guestfwd (UDP
guestfwd is not supported by QEMU at all).

NetBootInfo gains a last field, dns_server: [u8; 4], at offset 124 inside the
existing tail padding (size stays 128, the result offsets do not move). All
zero means the default 8.8.8.8, because the boot page is zero-filled, so
every other net test is unchanged. run_dns_lookup takes the configured
address.

net_driver_dns_mock sets it to 10.0.2.2 and asserts the guest resolves exactly
192.0.2.53 (TEST-NET-1, which cannot come from the real internet), answered
by kernel/tests/support/mock_dns.py (stdlib only, exits after one answered
query or a timeout). The CI step compiles the test first, starts the mock with
sudo for port 53, runs the test, and waits on the mock's PID so the step
fails unless the guest's query really reached it.

The old real-egress net_driver_dns stays local-only. This test no longer
proves real resolver reachability; it proves the smoltcp DNS socket, the UDP
path via the gateway, the exact answer and that the new field is honoured.

Verified on Windows: mock up passes, mock down fails with "no DNS answer
within poll bound", icmp, dhcp and the real-egress dns test still pass,
abi-consistency passes. NOT verified on Linux: the gateway mapping on
libslirp 4.7, sudo for port 53, and 127.0.0.1:53 being free on ubuntu-latest.
BootSectorInfo::parse, cluster_to_sector, the FSInfo parse/encode round
trip, find_reusable_slot and encode_short_name were not covered by any
fuzz target (the parser had proptest only). boot_sector_fuzz drives them from
arbitrary 512-byte input and asserts the parser's own invariants. A local run
under libFuzzer with ASan in WSL Fedora did 53,183 execs in 61 s with no
crash or sanitizer report. The seed corpus stays untracked (the corpus
directories are gitignored in every fuzz crate).

.github/workflows/fuzz.yml runs every fuzz target (capability-manager,
net-driver-host, and the four blk-driver-host targets) with libFuzzer on
workflow_dispatch (a "seconds" input, default 60) and a weekly schedule only.
It is deliberately not a PR gate. cargo-fuzz is pinned to 0.13.2 and compiled
on each run; fuzz artifacts are uploaded on failure.

Not yet run on GitHub: ASan on ubuntu-latest and the cargo-fuzz install are
unverified, and a workflow_dispatch workflow normally has to exist on the
default branch before it can be dispatched.
Records the M3 work (ADR 0010: governed session cleanup, usage-feed codes 8
and 9, the policy engine stays advisory), the mock-DNS test, the fuzz
workflow and the new boot_sector_fuzz target in CHANGELOG, STATUS,
BETA_CRITERIA and the release notes draft, and adds the ADR 0010 row to the
ADR index.

BETA_CRITERIA: M3 moves to MET (as scoped), so the header reads 4 MET as
scoped, 4 PARTIAL; its "partial point" sentence moves to Remains with the
M3-c decision. M1, D3 and D4 keep their statuses and gain final-scope
wording: no provisioning or slot reclamation in Beta (accounts are compiled-in
demo data, ids never reused by design, no close syscall), the blk quarantine
is permanent until the driver restarts, create/delete/grow stay
driver-internal, and spawn_instance has no production caller (clause (a)
reads "mechanism MET, test-only caller, ungoverned in practice").

STATUS: the stale "one compiled-in account" line and the reconcile counts
(2, 0, 0) are corrected, and the fuzz text now describes the manual/weekly
workflow. The new CI assertions are recorded as not yet observed in a CI run.
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.

2 participants