Skip to content

Commit 47820e8

Browse files
avrabeclaude
andauthored
test(vcr-mem): layer-2 cross-check on gust_kernel's flashed silicon budget (#242, #383) (#424)
jess is flashing gust_kernel.wasm with an integrator-ASSERTED `--shadow-stack-size 4096` on the Renode-M3 / STM32F100 rung (gale#65). This test is the layer-2 cross-check on that live budget: scry PROVES gust_kernel's worst-case shadow-stack depth is Bytes(16) (sp_global=0, no recursion, 6 reachable), so the proven depth clears the flashed 4096 with a 256x margin — the asserted budget is SOUND, not an under-reservation. layer-2 would auto-derive 16 B (ProvenStackDepth), 256x tighter than 4096 and 65536x under the 1 MiB declared-page default. Previously only the asserted 4096 was on record; gust_kernel's PROVEN depth was unmeasured. `layer2_gust_kernel_proven_depth_clears_flashed_budget_383` (synth-cli main.rs cfg(test)) extends the msgq end-to-end pattern to the fixture jess actually flies, asserting proven<=flashed so a scry bump that raised the proven depth above the budget reddens CI before silicon, not after. Frozen-safe: scry stays a DEV-dep under cfg(test); production bytes unchanged. Roadmap VCR-MEM-001 records the proven-vs-asserted finding. Verification: `cargo test -p synth-cli --bin synth layer2_` -> 2/2 pass; fmt + clippy -D warnings clean; rivet check zero non-xref errors. Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
1 parent 3a6321e commit 47820e8

2 files changed

Lines changed: 87 additions & 0 deletions

File tree

artifacts/verified-codegen-roadmap.yaml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1100,6 +1100,16 @@ artifacts:
11001100
the [0, sp_init) stack page); --shadow-stack-size errors honestly
11011101
(PR #388 scaffold). B = 4096 -> reserve B + 144 = ~4.2 KB, inside the
11021102
8 KiB STM32F100 part.
1103+
- SCRY-PROVEN DEPTH (measured 2026-06-22, dev-dep): scry proves
1104+
gust_kernel's worst-case shadow-stack depth is Bytes(16) (sp_global=0,
1105+
no recursion, 6 reachable) — so jess's flashed asserted B=4096 clears
1106+
the proven depth with a 256x margin (16 <= 4096): the live silicon-rung
1107+
budget is SOUND, not an under-reservation. layer-2 would auto-derive
1108+
16 B (ProvenStackDepth) — 256x tighter than the asserted 4096 and
1109+
65536x under the 1 MiB declared-page default. Locked by the dev-dep
1110+
test `layer2_gust_kernel_proven_depth_clears_flashed_budget_383`, so a
1111+
future scry bump raising the proven depth above the flashed budget
1112+
reddens CI before silicon, not after.
11031113
So layer-1 is unblocked and its geometry is the clean uniform-down-shift.
11041114
Remaining = the byte-changing retarget itself (re-base sp_init -> B; shift
11051115
every __synth_wasm_data static addend >= sp_init down by (sp_init - B),

crates/synth-cli/src/main.rs

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4214,6 +4214,83 @@ mod tests {
42144214
);
42154215
}
42164216

4217+
/// VCR-MEM-001 layer-2 SILICON-BUDGET SANITY CHECK (#242, #383, gale#65):
4218+
/// jess is flashing `gust_kernel.wasm` with an integrator-ASSERTED
4219+
/// `--shadow-stack-size 4096` on the Renode-M3 / STM32F100 rung. This test is
4220+
/// the layer-2 cross-check on that live budget: scry PROVES gust_kernel's
4221+
/// worst-case shadow-stack depth, and we assert the proven depth sits at or
4222+
/// below the 4096 jess flashed — i.e. the asserted budget is sound, not an
4223+
/// under-reservation. (Measured 2026-06-22: proven depth is 16 B, a 256x
4224+
/// margin under 4096 and a 65536x cut from the 1 MiB declared-page default.)
4225+
///
4226+
/// Frozen-safe: scry stays a DEV-dep under cfg(test); production bytes are
4227+
/// unchanged. Extends the msgq end-to-end test to the fixture jess actually
4228+
/// flies — so if a scry bump ever raised gust_kernel's proven depth above the
4229+
/// flashed budget, CI would surface it before silicon, not after.
4230+
#[test]
4231+
fn layer2_gust_kernel_proven_depth_clears_flashed_budget_383() {
4232+
use scry_analyze_core::{AnalysisConfig, StackBound, analyze};
4233+
use shadow_budget::{BudgetDecision, BudgetSource, StackDepthBound, budget_from_bound};
4234+
4235+
let fixture = std::path::Path::new(env!("CARGO_MANIFEST_DIR"))
4236+
.join("../../scripts/repro/gust_kernel.wasm");
4237+
let bytes = std::fs::read(&fixture).expect("the gale #91 gust_kernel fixture is in-tree");
4238+
4239+
let r = analyze(
4240+
bytes,
4241+
AnalysisConfig {
4242+
widening_threshold: None,
4243+
emit_diagnostics: false,
4244+
taint_policy: None,
4245+
},
4246+
)
4247+
.expect("scry analyzes the gust_kernel Core module");
4248+
4249+
// scry identifies a real shadow stack with a finite, non-recursive depth.
4250+
assert_eq!(
4251+
r.stack_usage.sp_global,
4252+
Some(0),
4253+
"gust_kernel's stack-pointer global is identified"
4254+
);
4255+
assert!(
4256+
!r.function_summaries.iter().any(|s| s.recursive),
4257+
"gust_kernel has no reachable recursion -> the depth is a finite proof"
4258+
);
4259+
4260+
// The proven worst-case depth, recorded as the layer-2 baseline for the
4261+
// fixture jess flashes (previously only the asserted 4096 was on record).
4262+
let proven = match r.stack_usage.max_stack_bytes {
4263+
StackBound::Bytes(n) => n,
4264+
other => panic!("expected a finite proven depth, got {other:?}"),
4265+
};
4266+
assert_eq!(proven, 16, "scry-proven gust_kernel shadow-stack depth (B)");
4267+
4268+
// THE LIVE-BUDGET SANITY CHECK: the proven depth must sit at or below the
4269+
// integrator-asserted budget jess flashed (`--shadow-stack-size 4096`),
4270+
// or that image under-reserves its stack on silicon.
4271+
const JESS_FLASHED_BUDGET: u64 = 4096;
4272+
assert!(
4273+
proven <= JESS_FLASHED_BUDGET,
4274+
"proven depth {proven} B must not exceed the flashed {JESS_FLASHED_BUDGET} B budget"
4275+
);
4276+
4277+
// And layer-2's own derivation ACCEPTS it (proven, not refused, not
4278+
// fallback): gust_kernel's sp_init is the 1 MiB declared-page top.
4279+
let decision = budget_from_bound(
4280+
StackDepthBound::Bytes(proven),
4281+
1_048_576,
4282+
Some(JESS_FLASHED_BUDGET as u32),
4283+
);
4284+
assert_eq!(
4285+
decision,
4286+
BudgetDecision::Use {
4287+
bytes: 16,
4288+
source: BudgetSource::ProvenStackDepth
4289+
},
4290+
"layer-2 derives a proven 16 B budget for gust_kernel (tighter than the asserted 4096)"
4291+
);
4292+
}
4293+
42174294
/// #235: a dissolved export's non-exported callee must be pulled into the
42184295
/// reachable set (so it lands in the relocatable object), while imports and
42194296
/// unreachable functions stay out.

0 commit comments

Comments
 (0)