use scry_analyze_core::{AnalysisConfig, GapKind, StackBound, analyze};
fn fixture(rel: &str) -> std::path::PathBuf {
std::path::Path::new(env!("CARGO_MANIFEST_DIR"))
.join("../..")
.join(rel)
}
#[test]
fn scry_proves_msgq_shadow_stack_budget_383() {
let bytes = std::fs::read(fixture("scripts/repro/msgq_put_359.wasm"))
.expect("the #359/#383 gust-family fixture is in-tree");
let cfg = AnalysisConfig {
widening_threshold: None,
emit_diagnostics: false,
taint_policy: None,
};
let r = analyze(bytes, cfg).expect("scry analyzes a valid Core module");
assert_eq!(
r.stack_usage.max_stack_bytes,
StackBound::Bytes(32),
"scry's proven worst-case shadow-stack depth for msgq_put"
);
assert!(
!r.function_summaries.iter().any(|s| s.recursive),
"msgq_put has no reachable recursion → the budget is a finite proof"
);
assert!(
!r.reachable_from_exports.is_empty(),
"reachable_from_exports is the sound superset synth prunes against"
);
assert!(
r.gaps
.iter()
.all(|g| !matches!(g.kind, GapKind::UnmodeledBranch)),
"msgq_put's gaps must be value-domain (no UnmodeledBranch), so the finite \
Bytes(32) bound is not perturbed by an unmodelled multi-target branch; \
got {:?}",
r.gaps
);
assert!(
matches!(r.stack_usage.max_stack_bytes, StackBound::Bytes(_)),
"a finite (Bytes) bound means scry resolved the call structure with no \
bound-affecting gap — FEAT-043 forces Unbounded/Unknown otherwise"
);
}
#[test]
fn scry_reachable_superset_is_open_world_sound_383_feat039() {
let cfg = || AnalysisConfig {
widening_threshold: None,
emit_diagnostics: false,
taint_policy: None,
};
let escaping = wat::parse_str(
r#"(module
(table (export "t") 1 funcref)
(elem (i32.const 0) 1)
(func (export "main"))
(func (result i32) i32.const 7))"#,
)
.expect("valid wat");
let r = analyze(escaping, cfg()).expect("scry analyzes the escaping module");
assert!(
r.reachable_from_exports.contains(&1),
"FEAT-039: a function in an EXPORTED funcref table is host-dispatchable ⇒ \
must be in the sound superset synth prunes against (got {:?}) — under \
scry <=2.4.0 this was dropped and synth would under-reserve",
r.reachable_from_exports
);
let closed = wat::parse_str(
r#"(module
(func (export "main"))
(func (result i32) i32.const 7))"#,
)
.expect("valid wat");
let r2 = analyze(closed, cfg()).expect("scry analyzes the closed module");
assert!(
r2.reachable_from_exports.contains(&0),
"the exported root is reachable (got {:?})",
r2.reachable_from_exports
);
assert!(
!r2.reachable_from_exports.contains(&1),
"a non-exported, uncalled, address-not-taken function MUST be pruned in a \
closed escape-free module — else the superset is vacuously 'all' (got {:?})",
r2.reachable_from_exports
);
}