diff --git a/Makefile b/Makefile index 1699d01..e2c9042 100644 --- a/Makefile +++ b/Makefile @@ -142,7 +142,7 @@ diligence-demo: echo "diligence-demo: host Path B golden lines ok" # Host sell-path: named attacks the kernel already refuses. Reuses -# blast / blast-hops / blast-nodes / bank-color / uncolored-compute / foreign-tenant-color / qos-credits / fence-not-ready / outside-slice / typed-window-sid / silent-remote / hbm-bw / xqueue-sid-override / set-sid-unbound / submit-sid / sid-budget / stage2-fault / softnoi-exhausted / hodge-harmonic-tree / hodge-curl-tree / hodge-quota / SoftCmdFirewall / firewall-ident-pa / greenctx-overcommit / greenctx-unbound / greenctx-exhausted / greenctx-busy / SoftSFI / SoftNoI-IS / PASID clips. +# blast / blast-hops / blast-nodes / bank-color / uncolored-compute / foreign-tenant-color / qos-credits / fence-not-ready / outside-slice / typed-window-sid / silent-remote / hbm-bw / xqueue-sid-override / set-sid-unbound / submit-sid / sid-budget / stage2-fault / softnoi-exhausted / hodge-harmonic-tree / hodge-curl-tree / hodge-quota / SoftCmdFirewall / firewall-ident-pa / greenctx-overcommit / greenctx-unbound / greenctx-exhausted / greenctx-busy / smmu-overlap / SoftSFI / SoftNoI-IS / PASID clips. # CI greps the [redteam] proof lines. Not a QEMU guest. REDTEAM_LOG := $(BUILD)/redteam.log @@ -177,6 +177,7 @@ red-team: grep -q "\\[redteam\\] attack=greenctx-unbound result=refused" $(REDTEAM_LOG) grep -q "\\[redteam\\] attack=greenctx-exhausted result=refused" $(REDTEAM_LOG) grep -q "\\[redteam\\] attack=greenctx-busy result=refused" $(REDTEAM_LOG) + grep -q "\\[redteam\\] attack=smmu-overlap result=refused" $(REDTEAM_LOG) grep -F -x -q "[redteam] fabric-class admit/refuse" $(REDTEAM_LOG) grep -F -x -q "[redteam] ATOMIC_ADD accept/reject" $(REDTEAM_LOG) grep -F -x -q "[softsfi] tensor=refused" $(REDTEAM_LOG) diff --git a/core/src/iommu.rs b/core/src/iommu.rs index 06ff0e1..2c74c13 100644 --- a/core/src/iommu.rs +++ b/core/src/iommu.rs @@ -1848,6 +1848,54 @@ pub fn run_stage2_fault_demo() -> Stage2FaultReport { } +/// Host red-team report for Soft-SMMU same-SID guest-PA overlap refuse. +/// +/// Sell line `[redteam] attack=smmu-overlap` — existing [`IommuMap::map`] path +/// only. Second pin overlapping guest PA on the **same** SID → +/// [`MapError::Overlap`]. Non-overlapping pin admits; peer SID may share PA. +/// **Not** CrossTenant / WrongStream / Stage2Fault / SubmitSid / SidBudget. +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub struct SmmuOverlapReport { + /// First in-range pin on SID admits. + pub first_ok: bool, + /// Overlapping guest-PA pin on same SID → `MapError::Overlap`. + pub overlap: bool, + /// Non-overlapping pin on same SID still admits (sibling contrast). + pub disjoint_ok: bool, +} + +impl SmmuOverlapReport { + pub fn all_ok(&self) -> bool { + self.first_ok && self.overlap && self.disjoint_ok + } +} + +/// Soft-SMMU `map` same-SID overlapping guest PA → [`MapError::Overlap`]. +/// Per-STE overlap honesty — not CrossTenant / WrongStream / Stage2Fault. +pub fn run_smmu_overlap_demo() -> SmmuOverlapReport { + use crate::caps::{CapKind, CapRights, Capability}; + use crate::types::TenantId; + + let mut iommu = IommuMap::new(); + let cap = Capability::new(CapKind::Memory, CapRights::MEM_FULL, 1, TenantId(1)) + .with_generation(1); + + let first = iommu.map(&cap, MapRequest::pin(PhysAddr(0x1000), 0x1000)); + let first_ok = first.is_ok(); + let overlap = iommu.map(&cap, MapRequest::pin(PhysAddr(0x1800), 0x1000)) + == Err(MapError::Overlap); + let disjoint_ok = iommu + .map(&cap, MapRequest::pin(PhysAddr(0x3000), 0x1000)) + .is_ok(); + + SmmuOverlapReport { + first_ok, + overlap, + disjoint_ok, + } +} + + #[cfg(test)] mod tests { use super::*; @@ -2048,6 +2096,15 @@ mod tests { ); } + #[test] + fn smmu_overlap_demo_refuses_same_sid_guest_pa() { + let r = run_smmu_overlap_demo(); + assert!(r.first_ok, "first pin admits"); + assert!(r.overlap, "overlapping guest PA → Overlap"); + assert!(r.disjoint_ok, "disjoint pin admits"); + assert!(r.all_ok()); + } + #[test] fn refuse_bad_range_and_same_stream_overlap() { let mut iommu = IommuMap::new(); diff --git a/core/src/lib.rs b/core/src/lib.rs index bc38c8f..308f207 100644 --- a/core/src/lib.rs +++ b/core/src/lib.rs @@ -88,8 +88,8 @@ pub use greenctx::{ }; pub use hodge::{run_hodge_quota_demo, FlowClass, HodgeError, HodgeQuota, HodgeQuotaReport}; pub use iommu::{ - run_stage2_fault_demo, AtcDumpLine, CdTableDump, InvCmd, IommuMap, MapError, MapRequest, - MappedRegion, MmId, SoftPte, SoftSmmuDump, Stage2FaultReport, SteConfig, SteTableDump, + run_smmu_overlap_demo, run_stage2_fault_demo, AtcDumpLine, CdTableDump, InvCmd, IommuMap, MapError, MapRequest, + MappedRegion, MmId, SoftPte, SoftSmmuDump, SmmuOverlapReport, Stage2FaultReport, SteConfig, SteTableDump, StreamId, StreamState, WalkResult, DEFAULT_STREAM, SET_SID, SID_BUDGET_PER_TENANT, SOFT_SMMU_IOVA_BASE, SOFT_SMMU_IPA_BASE, }; diff --git a/docs/DILIGENCE.md b/docs/DILIGENCE.md index 998a77a..72fe546 100644 --- a/docs/DILIGENCE.md +++ b/docs/DILIGENCE.md @@ -42,7 +42,7 @@ reserved. Freeze proof: `make design-win-standin`. Port skipped | Command | What they see | | --- | --- | | `make diligence-demo` | blast / pjrt / event + fence counts / softcct package ≪ broadcast + single-chiplet=noop + incorrect-elision=refused / `[scope] soft≠strict` / firewall / greenctx 70/30 + interference + migrate SID-sticky / cdt revoke descendants / softcp sparsify DROP / opinject resident+hot-add / partner-hello bad-exec | -| `make red-team` | named refuses + fabric-class + `ATOMIC_ADD` + `[softsfi] tensor=refused` + `[softsfi] heap=refused` + `[softsfi] unknown=refused` + `[softsfi] unknown-base=refused` + `typed-window-sid` + `hbm-bw` + `xqueue-sid-override` + `set-sid-unbound` + `submit-sid` + `sid-budget` + `stage2-fault` + `softnoi-exhausted` + `hodge-harmonic-tree` + `hodge-curl-tree` + `hodge-quota` + `firewall-ident-pa` + `greenctx-overcommit` + `greenctx-unbound` + `greenctx-exhausted` / `greenctx-busy` + `foreign-tenant-color` | +| `make red-team` | named refuses + fabric-class + `ATOMIC_ADD` + `[softsfi] tensor=refused` + `[softsfi] heap=refused` + `[softsfi] unknown=refused` + `[softsfi] unknown-base=refused` + `typed-window-sid` + `hbm-bw` + `xqueue-sid-override` + `set-sid-unbound` + `submit-sid` + `sid-budget` + `stage2-fault` + `softnoi-exhausted` + `hodge-harmonic-tree` + `hodge-curl-tree` + `hodge-quota` + `firewall-ident-pa` + `greenctx-overcommit` + `greenctx-unbound` + `greenctx-exhausted` / `greenctx-busy` + `smmu-overlap` + `foreign-tenant-color` | | `make partner-hello` | frozen `IreeHalCmd` → `IreeShapedCp`; bad exec refused | | `make mp-shim` | MicroPerceptron-shaped thin consumer (PR #83). Inspiration name only. Not a port. | | `make design-win-check` / `make design-win-standin` | blank they fill, or the IREE HAL research stand-in (not a partner). TRANSFER-only refused. | @@ -323,7 +323,7 @@ task-local AP_EL0 leaves + Soft SMMU” (no PAN on cortex-a72). | --- | --- | --- | | Host tests | `cargo test --workspace` | Caps + CDT properties, fabric, arenas, color, map, typed window stub, sched, SoftNPU, Laplacian, ELF, ramfs, bootfs, mmap, opkernel, sparsify, diligence-demo + red-team + accel-client + aether-mp-shim + design-win-check crates, partner-hello | | Diligence demo | `make diligence-demo` | Host Path B partner clip; greps `[blast]` / `[pjrt]` / `[event]` / `[softcct]` (package ≪ broadcast + `single-chiplet=noop` + `incorrect-elision=refused`) / `[scope] soft≠strict` / `[firewall]` / `[greenctx]` (incl. M3 interference + migrate SID-sticky) / `[cdt] revoke descendants ok` / `[softcp] sparsify DROP` / `[opinject] resident worker + hot-add sealed` / `[partner-hello] bad executable` + proves/does-not. No QEMU rebuild. Caps/CDT honesty ≠ CapTable milestone | -| Red-team clip | `make red-team` | Host stdout; greps `[redteam] attack=… result=refused` plus fabric-class / `ATOMIC_ADD` / `[softsfi] tensor=refused` / `[softsfi] heap=refused` / `[softsfi] unknown=refused` / `[softsfi] unknown-base=refused` / `typed-window-sid` / `hbm-bw` / `xqueue-sid-override` / `set-sid-unbound` / `submit-sid` / `sid-budget` / `stage2-fault` / `softnoi-exhausted` / `hodge-harmonic-tree` / `hodge-curl-tree` / `hodge-quota` / `firewall-ident-pa` / `greenctx-overcommit` / `greenctx-unbound` / `greenctx-exhausted` / `greenctx-busy` / `foreign-tenant-color` and the “what this is not” closer | +| Red-team clip | `make red-team` | Host stdout; greps `[redteam] attack=… result=refused` plus fabric-class / `ATOMIC_ADD` / `[softsfi] tensor=refused` / `[softsfi] heap=refused` / `[softsfi] unknown=refused` / `[softsfi] unknown-base=refused` / `typed-window-sid` / `hbm-bw` / `xqueue-sid-override` / `set-sid-unbound` / `submit-sid` / `sid-budget` / `stage2-fault` / `softnoi-exhausted` / `hodge-harmonic-tree` / `hodge-curl-tree` / `hodge-quota` / `firewall-ident-pa` / `greenctx-overcommit` / `greenctx-unbound` / `greenctx-exhausted` / `greenctx-busy` / `smmu-overlap` / `foreign-tenant-color` and the “what this is not” closer | | Design-win checker | `make design-win-check` | Loads sample filled worksheet; refuses unknown executable / SID 0 / TRANSFER-only. No pipes | | IREE HAL stand-in | `make design-win-standin` | Admits `docs/design-win/iree-hal-standin.toml`. Not a partner | | Partner hello | `make partner-hello-ci` | Frozen `IreeHalCmd` pack/submit + bad executable refuse; no QEMU | @@ -384,6 +384,7 @@ runs `examples/red-team` on the host and prints grep-able lines. It | SoftGreenPool migrate unbound | `run_greenctx_unbound_demo` — `SoftGreenPool::migrate_to_yield` → `GreenCtxError::Unbound` on never-bound queue (bound migrate keeps SID). **Not** set-sid-unbound Soft-CP Fault; not greenctx-overcommit; not HW MIG / BAR0 / SoftNPU | refused | | SoftGreenPool slot exhausted | `run_greenctx_exhausted_demo` — `SoftGreenPool::create` past `MAX_GREEN_CTX` → `GreenCtxError::Exhausted` (tiny in-budget fills admit; contrast after 70/30 → Overcommit). **Not** greenctx-overcommit SM/WQ; not SoftNoI Exhausted; not HW MIG / BAR0 / SoftNPU | refused | | SoftGreenPool migrate dest busy | `run_greenctx_busy_demo` — `SoftGreenPool::migrate_to_yield` → `GreenCtxError::Busy` when dest bound to another queue (`bind(lo,0); bind(hi,1); migrate 0→hi`). **Not** greenctx-unbound; not overcommit/exhausted; not xqueue-sid-override Soft-CP Busy; not HW MIG / BAR0 / SoftNPU | refused | +| Soft-SMMU same-SID guest-PA overlap | `run_smmu_overlap_demo` — Soft-SMMU `map` → `MapError::Overlap` on overlapping guest PA same SID (disjoint admits). **Not** CrossTenant / WrongStream / Stage2Fault / SubmitSid / SidBudget | refused | Expected stdout (CI greps these): @@ -418,6 +419,7 @@ Expected stdout (CI greps these): [redteam] attack=greenctx-unbound result=refused [redteam] attack=greenctx-exhausted result=refused [redteam] attack=greenctx-busy result=refused +[redteam] attack=smmu-overlap result=refused [redteam] fabric-class admit/refuse [redteam] ATOMIC_ADD accept/reject [softsfi] tensor=refused diff --git a/examples/red-team/README.md b/examples/red-team/README.md index b36f925..ce31308 100644 --- a/examples/red-team/README.md +++ b/examples/red-team/README.md @@ -4,7 +4,7 @@ Host stdout a buyer can grep. Reuses `run_blast_demo`, `run_blast_hops_demo`, `run_blast_nodes_demo`, `run_bank_color_demo`, `run_uncolored_compute_demo`, `run_foreign_tenant_color_demo`, `run_qos_credits_demo`, `run_fence_not_ready_demo`, `run_outside_slice_demo`, `run_typed_window_sid_demo`, `run_silent_remote_demo`, `run_hbm_bw_demo`, `run_xqueue_sid_override_demo`, `run_set_sid_unbound_demo`, `run_submit_sid_demo`, `run_sid_budget_demo`, `run_stage2_fault_demo`, `run_softnoi_exhausted_demo`, -`run_hodge_harmonic_tree_demo`, `run_hodge_curl_tree_demo`, `run_hodge_quota_demo`, `run_firewall_demo`, `run_firewall_ident_pa_demo`, `run_greenctx_overcommit_demo`, `run_greenctx_unbound_demo`, `run_greenctx_exhausted_demo`, `run_greenctx_busy_demo`, `run_softsfi_demo`, +`run_hodge_harmonic_tree_demo`, `run_hodge_curl_tree_demo`, `run_hodge_quota_demo`, `run_firewall_demo`, `run_firewall_ident_pa_demo`, `run_greenctx_overcommit_demo`, `run_greenctx_unbound_demo`, `run_greenctx_exhausted_demo`, `run_greenctx_busy_demo`, `run_smmu_overlap_demo`, `run_softsfi_demo`, `run_softnoi_demo`, and `run_sva_demo`. Not a new isolator, not a QEMU guest, not a slide. Blast-nodes needle: `[redteam] attack=blast-nodes result=refused` (`admit_nodes` → `BlastRadius`; @@ -72,6 +72,8 @@ greenctx-overcommit SM/WQ ceiling; not SoftNoI Exhausted; not HW MIG / BAR0 / So Greenctx-busy needle: `[redteam] attack=greenctx-busy result=refused` (`SoftGreenPool::migrate_to_yield` when dest bound to another queue → `GreenCtxError::Busy`; not greenctx-unbound; not overcommit/exhausted; not xqueue-sid-override Soft-CP Busy; not HW MIG / BAR0 / SoftNPU). SoftSFI tensor needle: `[softsfi] tensor=refused` (`SoftOp::Tensor` → `Unmodeled`; +Smmu-overlap needle: `[redteam] attack=smmu-overlap result=refused` +(Soft-SMMU `map` same-SID overlapping guest PA → `MapError::Overlap`; disjoint admits; not CrossTenant / WrongStream / Stage2Fault / SubmitSid / SidBudget). heap line stays separate). SoftSFI unknown needle: `[softsfi] unknown=refused` / `[softsfi] unknown-base=refused` (bad opcode / illegal width → `Unmodeled`; tensor/heap lines stay separate; not AddImm deepen). diff --git a/examples/red-team/src/main.rs b/examples/red-team/src/main.rs index d975778..07f96a5 100644 --- a/examples/red-team/src/main.rs +++ b/examples/red-team/src/main.rs @@ -5,7 +5,7 @@ //! `run_bank_color_demo`, `run_uncolored_compute_demo`, //! `run_foreign_tenant_color_demo`, `run_qos_credits_demo`, `run_fence_not_ready_demo`, `run_outside_slice_demo`, `run_typed_window_sid_demo`, //! `run_silent_remote_demo`, `run_hbm_bw_demo`, `run_xqueue_sid_override_demo`, -//! `run_set_sid_unbound_demo`, `run_submit_sid_demo`, `run_sid_budget_demo`, `run_stage2_fault_demo`, `run_softnoi_exhausted_demo`, `run_hodge_harmonic_tree_demo`, `run_hodge_curl_tree_demo`, `run_hodge_quota_demo`, `run_firewall_demo`, `run_firewall_ident_pa_demo`, `run_greenctx_overcommit_demo`, `run_greenctx_unbound_demo`, `run_greenctx_exhausted_demo`, `run_greenctx_busy_demo`, +//! `run_set_sid_unbound_demo`, `run_submit_sid_demo`, `run_sid_budget_demo`, `run_stage2_fault_demo`, `run_softnoi_exhausted_demo`, `run_hodge_harmonic_tree_demo`, `run_hodge_curl_tree_demo`, `run_hodge_quota_demo`, `run_firewall_demo`, `run_firewall_ident_pa_demo`, `run_greenctx_overcommit_demo`, `run_greenctx_unbound_demo`, `run_greenctx_exhausted_demo`, `run_greenctx_busy_demo`, `run_smmu_overlap_demo`, //! `run_softsfi_demo`, `run_softnoi_demo`, `run_sva_demo`). //! This crate does not invent a new isolation mechanism. //! @@ -62,13 +62,15 @@ //! `create` past `MAX_GREEN_CTX` slots → `GreenCtxError::Exhausted` (not greenctx-overcommit SM/WQ ceiling; //! not SoftNoI Exhausted; not HW MIG / BAR0 / SoftNPU). Greenctx-busy is SoftGreenPool //! `migrate_to_yield` when dest is bound to another queue → `GreenCtxError::Busy` (not greenctx-unbound; -//! not greenctx-overcommit / exhausted; not xqueue-sid-override Soft-CP Busy; not HW MIG / BAR0 / SoftNPU). The closer names what this is **not**: +//! not greenctx-overcommit / exhausted; not xqueue-sid-override Soft-CP Busy; not HW MIG / BAR0 / SoftNPU). Smmu-overlap is Soft-SMMU +//! `map` same-SID overlapping guest PA → `MapError::Overlap` (disjoint pin admits; not CrossTenant / +//! WrongStream / Stage2Fault / SubmitSid / SidBudget). The closer names what this is **not**: //! confidential GPU, HW MIG, hardware SMMU (Soft SMMU is software). //! //! Run: `make red-team` or `cargo run -p aether-redteam`. use aether_core::blast::run_blast_demo; -use aether_core::iommu::run_stage2_fault_demo; +use aether_core::iommu::{run_smmu_overlap_demo, run_stage2_fault_demo}; use aether_core::sid::{run_sid_budget_demo, run_submit_sid_demo}; use aether_core::fence::run_fence_not_ready_demo; use aether_core::noi::{run_softnoi_demo, run_softnoi_exhausted_demo}; @@ -120,6 +122,7 @@ const LINE_GREENCTX_OVERCOMMIT: &str = "[redteam] attack=greenctx-overcommit res const LINE_GREENCTX_UNBOUND: &str = "[redteam] attack=greenctx-unbound result=refused"; const LINE_GREENCTX_EXHAUSTED: &str = "[redteam] attack=greenctx-exhausted result=refused"; const LINE_GREENCTX_BUSY: &str = "[redteam] attack=greenctx-busy result=refused"; +const LINE_SMMU_OVERLAP: &str = "[redteam] attack=smmu-overlap result=refused"; const LINE_CLASS: &str = "[redteam] fabric-class admit/refuse"; const LINE_ATOMIC: &str = "[redteam] ATOMIC_ADD accept/reject"; const LINE_TENSOR: &str = "[softsfi] tensor=refused"; @@ -162,6 +165,7 @@ struct RedTeamReport { greenctx_unbound: bool, greenctx_exhausted: bool, greenctx_busy: bool, + smmu_overlap: bool, class: bool, atomic: bool, tensor: bool, @@ -202,6 +206,7 @@ impl RedTeamReport { && self.greenctx_unbound && self.greenctx_exhausted && self.greenctx_busy + && self.smmu_overlap && self.class && self.atomic && self.tensor @@ -240,6 +245,7 @@ fn run_redteam() -> RedTeamReport { let greenctx_unbound = run_greenctx_unbound_demo(); let greenctx_exhausted = run_greenctx_exhausted_demo(); let greenctx_busy = run_greenctx_busy_demo(); + let smmu_overlap = run_smmu_overlap_demo(); let sfi = run_softsfi_demo(); let noi = run_softnoi_demo(); let sva = run_sva_demo(); @@ -333,6 +339,9 @@ fn run_redteam() -> RedTeamReport { // SoftGreenPool::migrate_to_yield dest bound elsewhere → Busy. // Not greenctx-unbound; not overcommit/exhausted; not xqueue Soft-CP Busy; not HW MIG. greenctx_busy: greenctx_busy.all_ok(), + // Soft-SMMU map same-SID overlapping guest PA → Overlap. + // Not CrossTenant / WrongStream / Stage2Fault / SubmitSid / SidBudget. + smmu_overlap: smmu_overlap.all_ok(), // Fabric-class tag: Gradient admits; second Curl refuses (ring). class: noi.class_grad_admit && noi.class_curl_refuse, // SID-proved toy fetch-add: in-bounds accept, foreign span Oob. @@ -398,6 +407,7 @@ fn print_clip(r: &RedTeamReport) { emit(r.greenctx_unbound, LINE_GREENCTX_UNBOUND); emit(r.greenctx_exhausted, LINE_GREENCTX_EXHAUSTED); emit(r.greenctx_busy, LINE_GREENCTX_BUSY); + emit(r.smmu_overlap, LINE_SMMU_OVERLAP); emit_tagged(r.class, LINE_CLASS); emit_tagged(r.atomic, LINE_ATOMIC); emit_tagged(r.tensor, LINE_TENSOR); @@ -500,6 +510,10 @@ mod tests { r.greenctx_busy, "SoftGreenPool::migrate_to_yield dest bound elsewhere → Busy" ); + assert!( + r.smmu_overlap, + "Soft-SMMU map same-SID overlapping guest PA → Overlap" + ); assert!(r.class, "fabric-class Gradient admit / Curl refuse"); assert!(r.atomic, "ATOMIC_ADD accept/reject"); assert!(r.tensor, "SoftSFI tensor named refuse"); @@ -583,6 +597,10 @@ mod tests { LINE_GREENCTX_BUSY, "[redteam] attack=greenctx-busy result=refused" ); + assert_eq!( + LINE_SMMU_OVERLAP, + "[redteam] attack=smmu-overlap result=refused" + ); assert_eq!(LINE_CLASS, "[redteam] fabric-class admit/refuse"); assert_eq!(LINE_ATOMIC, "[redteam] ATOMIC_ADD accept/reject"); assert_eq!(LINE_TENSOR, "[softsfi] tensor=refused"); @@ -616,6 +634,7 @@ mod tests { LINE_GREENCTX_UNBOUND, LINE_GREENCTX_EXHAUSTED, LINE_GREENCTX_BUSY, + LINE_SMMU_OVERLAP, ] { assert!( line.starts_with("[redteam] attack=") && line.ends_with(" result=refused"),