ADR-080 — PAN enable + EL1-vs-EL0 access fault (ADR-054 reopen gate 3–4)
- Status: Accepted (enable + fault Verified on default
-cpu cortex-a76). Meets ADR-054 reopen gate items 3–4: enable PSTATE.PAN /MSR PANand prove an EL1 load of an EL0-accessible page faults (fail-closed); honesty ledger + smoke updated. Does not claim “EL0 isolated.” Taken SError reopen is ADR-081 (Accepted — still blocked). - Date: 2026-09-19
- Tracks #125 mile (2). Builds on ADR-079 (gate items 1–2). CloudAgent HELD — agent-box / amd64 workstation / GHA only.
Context
ADR-054 locked PAN enable until a follow-up ADR after FEAT_PAN CPU reopen. ADR-079 met gate items 1–2 (-cpu cortex-a76, pan: present). Gate item 3 required this ADR: enable PSTATE.PAN and prove EL1-vs-EL0 fault. Gate item 4: ledger + smoke for enable; keep a57 pan: absent historical.
ADR-013 already named the enable mile: with PSTATE.PAN set, an EL1 data access to a page that is unprivileged-accessible must take a permission fault. Umbrella “EL0 isolated” stays non-claim (ADR-055 / ADR-060). Never yank _start.
Hypotheses
| ID | Option | Notes |
|---|---|---|
| H1 (chosen) | After ID-field pan: present, MSR PAN, #1; map an EL0-RO page; EL1 LDR must permission-fault with SPSR.PAN set; print pan: enabled / pan: el1-fault; leave PAN set; syscall uaccess clears PAN around copies. | Fail-closed if fault missing or SPSR.PAN clear. Preserves net/FAT/samples. |
| H2 (rejected) | Enable for the probe only, then clear PAN for the rest of boot. | Weaker than “enable”; uaccess wrapping is small and honest. |
| H3 (rejected) | Claim “EL0 isolated” from this mile alone. | ADR-055 §B still unmet (taken SError, _start stay, umbrella sponsor accept). |
| H4 (rejected) | Invent Verified without a caught fault / silent -cpu. | Honesty fail. |
Decision
- Enable.
src/pan.rsobserve_enable(afterframe::init) executesMSR PAN, #1on FEAT_PAN CPUs (ID_AA64MMFR1_EL1.PAN != 0). Serialpan: enabled. - EL1-vs-EL0 fault. Map an EL0-accessible RO page (
map_el0_ro); with PAN clear, EL1 preload must succeed; with PAN set, EL1LDRmust take a current-EL permission DABORT. Handler printspan: el1-faultand requires SPSR.PAN (bit 22) set at the fault (fail-closed otherwise). Note: under QEMU 10.0.13 TCG,MRS PANmay read 0 even while SPSR.PAN and the permission check prove enable — SPSR is the probe of record here. - Leave PAN set. After a successful probe, PSTATE.PAN stays 1.
pan::with_user_accessclears/restores around syscallcopy_user/copy_to_userso intentional EL0-accessible copies keep working. - Serial + smoke. Keep
pan: id=/pan: present(ADR-079). Requirepan: enabled/pan: el1-fault. Rejectpan: enable missed/ unexpectedpan: absent. Historical a57pan: absentrows remain historical. - Target feature.
aarch64-ctos.jsonadds+panso the assembler accepts thepanPSTATE name (still-cpu cortex-a76in scripts). - Still non-claim. Umbrella “EL0 isolated” unchanged. Taken SError → ADR-081 (blocked). Never yank
_start. No Guest Linux / containers marketing.
Honesty
Say: “on -cpu cortex-a76 (ADR-079), PSTATE.PAN is enabled (ADR-080); EL1 load of an EL0-accessible page faults (pan: el1-fault); umbrella EL0 isolation stays non-claim.” Do not say:
- “EL0 isolated” / “secure OS”
- taken lower-EL SError is Verified
- PAN worked on cortex-a57 without a CPU change
- Guest Linux / containers / immutable-OS
Consequences
src/pan.rs,src/exception.rs(arm + SPSR.PAN check),src/syscall.rs(uaccess),src/main.rs,scripts/qemu-smoke.sh,aarch64-ctos.json.- Docs: this ADR; ADR-054 / ADR-055 / ADR-060 / ADR-026 / ADR-079 cross-links; honesty ledger; roadmap P-SEC-3k; threat-model v1.55; SUMMARY; el0 / limits / security light touch.
- Preserve all existing net/FAT/sample smoke greps. CloudAgent HELD. Do not self-merge (ADR-002).
Follow-on note (2026-09-29, ADR-098)
ADR-098 found that PAN, once enabled here, was not held while the kernel handled exceptions from EL0 or after it returned from an EL0 trip: SCTLR_EL1.SPAN was left at 1 (so exception entry from EL0 did not set PSTATE.PAN), and return_from_el0 resumed EL1 with SPSR.PAN clear. The pan: el1-fault probe above runs once at boot, before any EL0 trip, so it could not see this. ADR-098 clears SPAN after a successful enable, sets SPSR.PAN on return_from_el0, and adds fail-closed live checks with a negative build. In that work MRS PAN read 1 whenever a fault proved PAN set, so the “MRS PAN may read 0” note in Decision 2 is not reproduced there. This ADR’s text is kept as the record of its time.