feat(vcr-mem): layer-2 shadow-stack budget decision logic, frozen-safe (#242, #383)#421
Merged
Merged
Conversation
#242, #383) VCR-MEM-001 layer-2 derives the shadow-stack budget B from a PROVEN worst-case stack depth instead of an integrator-asserted number (layer-1, #383). This lands the DECISION LOGIC only — deliberately dep-free and decoupled from the scry production-dep decision (a 28-crate Bazel cascade that is a separate, user-gated step). `synth-cli/src/shadow_budget.rs`: pure `budget_from_bound(bound, sp_init, fallback) -> BudgetDecision` over a synth-OWNED `StackDepthBound` enum (Bytes(u64)|Unbounded|Unknown), so the logic + its 9 unit tests compile WITHOUT scry in the build graph. The adapter from `scry_analyze_core::StackBound` lives at the (gated) call site. Scope is primary-source-confirmed (scry-sai-core-1.12 lib.rs:276-281: max_stack_bytes = "the deepest weighted path … each function weighted by the frame its prologue subtracts from the __stack_pointer global … guest shadow-stack only") ⇒ Bytes(n) yields source=ProvenStackDepth labelled "proven stack DEPTH, asserted no-heap" — NOT a bare "proven footprint" (the #383 overclaim shape is explicitly guarded). Refuses when proven depth > sp_init, naming it as the ORIGINAL image under-provisioning its stack. Unbounded/Unknown ⇒ AssertedFallback or honest-refuse; Unknown NEVER yields ProvenStackDepth even with a fallback. B rounded up to 16-byte AAPCS/LLVM stack alignment (rounding up preserves B >= proven depth). Frozen-safe: no call site yet (`#[allow(dead_code)]`), no byte-changing codegen, default behaviour unchanged. The scry dep-architecture choice and the analyze()→budget wiring are the remaining gated steps, recorded in the roadmap (2a user decision / 2b adapter+wiring). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Codecov Report❌ Patch coverage is
📢 Thoughts on this report? Let us know! |
avrabe
added a commit
that referenced
this pull request
Jun 22, 2026
…n refuses a proven budget (#242, #383) (#425) The msgq/gust tests cover layer-2's PROVEN (finite) path; the soundness of the whole derivation rests on the complementary REFUSAL path working — an unbounded shadow stack must never receive a finite budget, or the image silently under-reserves and overflows on silicon. #421 covers this with synthetic bounds only; this anchors it against REAL scry output. `scripts/repro/recursive_shadow_stack.wat` recurses through the shadow stack (each activation decrements the SP global by a 16 B frame), so the worst-case depth is unbounded. Empirically scry classifies it exactly: sp_global identified, recursive=true, max_stack_bytes=Unbounded. `layer2_unbounded_recursion_refuses_proven_budget_242` (synth-cli main.rs cfg(test)) asserts that scry behaviour AND that `budget_from_bound` never yields a ProvenStackDepth for it — with a fallback it returns AssertedFallback (not proven), without one it honestly refuses. This guards the upstream assumption the entire honest-fail gate depends on: a scry regression that returned a finite bound for an unbounded stack — the one failure mode that would silently under-reserve on silicon — would redden CI. Frozen-safe: scry + wat stay test-only (cfg(test)); production bytes unchanged, no MODULE.bazel pin. Roadmap VCR-MEM-001 records the proven+honest-fail coverage. Verification: `cargo test -p synth-cli --bin synth layer2_` -> 3/3 pass; fmt + clippy -D warnings clean; rivet check zero non-xref errors. Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
avrabe
added a commit
that referenced
this pull request
Jun 22, 2026
, #394) (#440) First release of the north-star verified-codegen campaign. Pin sweep 0.11.51 -> 0.12.0 (workspace + all path-dep versions + MODULE.bazel + Cargo.lock), CHANGELOG, and VCR-DBG-001 roadmap status -> implemented. Ships: - VCR-DBG-001 `--debug-line` DWARF emission (PR A #427 / B #429 / C #430): debugger-readable .debug_* mapping ARM .text -> wasm source lines, with .rel.debug_* relocations verified end-to-end against arm-none-eabi-ld. Purely additive; default build bit-identical. - VCR-MEM-001 layer-2 frozen-safe budget decision logic + oracles (#421-#425). - gimli 0.33 adaptation (#439) + wast 252 (#438). Minor bump: new user-facing `--debug-line` feature (semver-correct). Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
VCR-MEM-001 layer-2 derives the shadow-stack budget
Bfrom a proven worst-case stack depth instead of the integrator-asserted number that layer-1 (#383, v0.11.51) trusts. This PR lands the decision logic only — the dep-free core — ahead of the gated scry-production-dep wiring.crates/synth-cli/src/shadow_budget.rs: a pureover a synth-owned
StackDepthBoundenum (Bytes(u64) | Unbounded | Unknown). Because the enum is synth's own (notscry_analyze_core::StackBound), the logic and its 9 unit tests compile and run with scry absent from the build graph — and, critically, without scry's ~28-crate transitive closure entering the Bazelrust_binarycompile. The adapter from scry's type lives at the future (gated) call site.Soundness scope — "proven stack DEPTH, asserted no-heap"
Confirmed against the primary source (
scry-sai-core-1.12lib.rs:276-281):max_stack_bytesis "the deepest weighted path through the call graph, each function weighted by the frame its prologue subtracts from the__stack_pointerglobal … guest shadow-stack only." That is exactly the[0, sp_init)regionBcontrols.Bytes(n)→source = ProvenStackDepth,B = align16(n)— labelled "proven stack depth, asserted no-heap". Never restated as a bare "proven footprint" (the native-pointer-abi: size the wasm .bss to data high-water-mark, not the full declared memory page (blocks tiny-RAM targets) #383 overclaim shape is explicitly guarded).Bytes(n)withn > sp_init→ refuse, naming it as the original image under-provisioning its stack (a latent source bug surfaced for free, not a generic refuse).Unbounded/Unknown→AssertedFallbackif a fallback is given, else honest refuse with the cause.Unknownnever yieldsProvenStackDepth, even with a fallback (unit-tested).Brounded up to 16-byte AAPCS/LLVM stack alignment — rounding up preservesB ≥ proven depth, so soundness is not weakened.Frozen-safe
No call site yet (
#[allow(dead_code)]), no byte-changing codegen, default behaviour unchanged. Frozen fixtures (control_step0x00210A55, flat+inlined flight_algo0x07FDF307, divseam) are untouched by construction.Remaining gated steps (recorded in the roadmap)
scry-sai-coredev-dep → production dep pulls its closure into the Bazel graph (synth-cliis built there,crates/BUILD.bazel:230; MODULE.bazel usescrate.from_specs(), likely because z3 needsstatic-link-z3). Choice among {from_cargo migration, feature-gate the scry call out of the Bazel build, ~28 manual specs} — not a unilateral call.scry::StackBound → StackDepthBoundadapter at a call site runninganalyze(), routed into the layer-1 shrink; opt-in (--shadow-stack-size auto);verifies-link scry SCRY-001.Verification
cargo test -p synth-cli --bin synth shadow_budget→ 9/9 passcargo fmt --checkclean;cargo clippy -p synth-cli --all-targets -- -D warningscleanrivet check→ zero non-xref errorsRefs #242 (VCR epic), #383 (layer-1).
🤖 Generated with Claude Code