Skip to content

fix(syscalls): raise the private-input clamp to 512 MiB#843

Merged
MauroToscano merged 4 commits into
mainfrom
fix/max-private-input-size
Jul 17, 2026
Merged

fix(syscalls): raise the private-input clamp to 512 MiB#843
MauroToscano merged 4 commits into
mainfrom
fix/max-private-input-size

Conversation

@Oppen

@Oppen Oppen commented Jul 16, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • 64 MiB was sized for the old naive-recursion VmProof case. Real proofs at production options (blowup=2, 219 queries) are much larger — ethrex/20 proves to ~231 MiB monolithic, and a continuation bundle carries one such proof per epoch.
  • Bumps both sides of the clamp together: the executor's cap (executor::vm::memory::MAX_PRIVATE_INPUT_SIZE) and the guest's mirrored constant (syscalls::syscalls::MAX_PRIVATE_INPUT_SIZE), so an honest length prefix stays within bound on both — a prior version of this change bumped only the host side and silently truncated real guest inputs.

Test plan

  • cargo test -p lambda-vm-prover --lib (continuation page-count regression test updated: 257 → 2049 pages)
  • cargo test -p executor --test flamegraph test_run_with_flamegraph_returns_generator_on_executor_new_failure

64 MiB was sized for the old naive-recursion VmProof case. Real proofs
at production options (blowup=2, 219 queries) are much larger -
ethrex/20 proves to ~231 MiB monolithic, and a continuation bundle
carries one such proof per epoch. Bump both sides of the clamp
(executor's cap and the guest's mirrored constant) together so an
honest length prefix is always within bound on both.
@github-actions

github-actions Bot commented Jul 16, 2026

Copy link
Copy Markdown

Benchmark Results for modified programs 🚀

Command Mean [ms] Min [ms] Max [ms] Relative
head hashmap 137.0 ± 2.5 133.3 141.0 1.00

@github-actions

Copy link
Copy Markdown

Benchmark Results for unmodified programs 🚀

Command Mean [ms] Min [ms] Max [ms] Relative
base binary_search 59.0 ± 0.8 57.8 60.3 1.00
head binary_search 59.6 ± 1.5 58.2 63.4 1.01 ± 0.03
Command Mean [ms] Min [ms] Max [ms] Relative
base bitwise_ops 58.3 ± 1.2 57.5 60.6 1.00
head bitwise_ops 59.3 ± 1.5 57.8 61.1 1.02 ± 0.03
Command Mean [ms] Min [ms] Max [ms] Relative
base ecsm 3.1 ± 0.0 3.1 3.2 1.00
head ecsm 3.2 ± 0.1 3.1 3.4 1.02 ± 0.04
Command Mean [ms] Min [ms] Max [ms] Relative
base fibonacci_26 62.6 ± 0.7 62.2 64.5 1.00
head fibonacci_26 63.6 ± 0.9 62.5 64.8 1.02 ± 0.02
Command Mean [ms] Min [ms] Max [ms] Relative
base keccak 128.4 ± 3.1 123.6 134.0 1.00 ± 0.04
head keccak 128.4 ± 4.3 122.3 136.1 1.00
Command Mean [ms] Min [ms] Max [ms] Relative
base matrix_multiply 64.8 ± 0.8 64.2 66.5 1.00
head matrix_multiply 65.4 ± 1.2 63.8 67.2 1.01 ± 0.02
Command Mean [ms] Min [ms] Max [ms] Relative
base modular_exp 59.2 ± 0.8 57.9 60.1 1.00 ± 0.02
head modular_exp 59.1 ± 0.7 58.4 60.2 1.00
Command Mean [ms] Min [ms] Max [ms] Relative
base quicksort 62.9 ± 0.4 62.0 63.8 1.00
head quicksort 64.1 ± 3.9 62.0 75.1 1.02 ± 0.06
Command Mean [ms] Min [ms] Max [ms] Relative
base sieve 64.6 ± 0.4 64.1 65.5 1.00
head sieve 68.2 ± 10.8 64.2 98.9 1.06 ± 0.17
Command Mean [ms] Min [ms] Max [ms] Relative
base sum_array 74.7 ± 0.5 74.1 75.7 1.00
head sum_array 75.7 ± 1.9 73.8 80.3 1.01 ± 0.03
Command Mean [ms] Min [ms] Max [ms] Relative
base syscall_commit 91.9 ± 1.2 90.9 94.3 1.00 ± 0.01
head syscall_commit 91.6 ± 0.4 91.1 92.4 1.00

@Oppen

Oppen commented Jul 16, 2026

Copy link
Copy Markdown
Collaborator Author

/ai-review

@github-actions

Copy link
Copy Markdown

Codex Code Review

  • Highexecutor/src/vm/memory.rs:47: Raising the cap to 512 MiB is incompatible with the sparse HashMap<u64, [u8; 4]> backing. A maximum input creates roughly 134 million hash entries—many gigabytes of overhead—plus the original buffer and executor/prover clones, likely causing OOM instead of supporting the advertised proof size. Use dense/page-backed storage or retain a feasible cap. The updated flamegraph test also allocates 512 MiB merely to test rejection.

@claude

claude Bot commented Jul 16, 2026

Copy link
Copy Markdown
Contributor

Reviewed — this is a clean, well-scoped constant bump (64 MiB → 512 MiB) with no issues found.

Checks performed:

  • u32 prefix: 512 MiB (0x20000000) fits in the 4-byte length prefix; store_private_inputs still guards with u32::try_from.
  • Address space: region now spans 0xFF000000..0x11F000004 in 64-bit space — clear of STACK_TOP (0xFFFF…FFF0) and the guest heap ceiling (0xC0000000). The old 64 MiB region already crossed the 4 GiB mark, so there's no new 32-bit boundary concern.
  • Both mirrored constants (executor::vm::memory::MAX_PRIVATE_INPUT_SIZE and syscalls::MAX_PRIVATE_INPUT_SIZE) are bumped together, which correctly addresses the prior host-only bump that silently truncated guest inputs.
  • All hardcoded references for the old value/page-count are updated (memory.rs, syscalls.rs, flamegraph test, continuation page-count test); grep found no stragglers.
  • Page math is correct: (512 MiB + 4).div_ceil(256 KiB) = 2049, matching the updated assertion/comment.
  • No eager MAX_PRIVATE_INPUT_SIZE-sized allocation is introduced — it's used only in bounds checks and page-span arithmetic.

One inherent tradeoff worth noting (not a defect): max_private_input_pages() rises 257 → 2049, so a malicious continuation bundle can now force AIR sizing for up to ~8× more private-input pages before rejection. This is the expected cost of supporting real-sized proofs.

LGTM.

@github-actions

Copy link
Copy Markdown

AI Review

PR #843 · 4 changed files

Findings

Status Sev Location Finding Found by
confirmed medium executor/src/vm/memory.rs:265 512 MiB private input blows up executor Memory hashmap kimi
openrouter/moonshotai/kimi-k2.7-code
confirmed medium prover/src/tests/prove_elfs_tests.rs:2821 Test no longer exercises the "exceeds max" code path it claims to test moonmath
zro/minimax-m3
confirmed low executor/tests/flamegraph.rs:895 Test allocates ~512 MiB to exercise the oversized-input failure path glm
openrouter/z-ai/glm-5.2
kimi
openrouter/moonshotai/kimi-k2.7-code
confirmed low prover/src/continuation.rs:1500 Stale 64 MiB reference in test comment kimi
openrouter/moonshotai/kimi-k2.7-code
moonmath
zro/minimax-m3

Status column reflects the verdict from the verifier: deepseek-verifier (openrouter/deepseek/deepseek-v4-pro).

AI-001: 512 MiB private input blows up executor Memory hashmap
  • Status: confirmed
  • Severity: medium
  • Location: executor/src/vm/memory.rs:265
  • Found by: kimi:openrouter/moonshotai/kimi-k2.7-code
  • Verified by: deepseek-verifier:openrouter/deepseek/deepseek-v4-pro
  • Rejected by: -

Claim

store_private_inputs writes the full input via set_bytes_aligned, which inserts one HashMap entry per 4-byte word. With the new 512 MiB cap this creates ~134M entries, consuming several GB of memory for a max-size input and risking OOM or swapping even on server hardware.

Evidence

set_bytes_aligned loops over inputs.chunks(4) and calls self.cells.insert(addr, bytes) for each chunk (lines 265-280). Memory::cells is a U64HashMap<[u8; 4]>, so a 512 MiB input becomes ~134 million entries. This is a large constant-factor blow-up on top of the already-allocated input Vec.

Suggested fix

Re-evaluate Memory's private-input backing before shipping the larger cap: either store the region as a dense Vec<u8> keyed by PRIVATE_INPUT_START_INDEX, or at least warn that 512 MiB inputs require multiple GB of executor RAM. A denser representation would avoid making Memory a memory bottleneck for recursion bundles.

AI-002: Test no longer exercises the "exceeds max" code path it claims to test
  • Status: confirmed
  • Severity: medium
  • Location: prover/src/tests/prove_elfs_tests.rs:2821
  • Found by: moonmath:zro/minimax-m3
  • Verified by: deepseek-verifier:openrouter/deepseek/deepseek-v4-pro
  • Rejected by: -

Claim

test_verify_rejects_num_private_input_pages_exceeds_max hardcodes num_private_input_pages: 1000. After MAX_PRIVATE_INPUT_SIZE was raised from 64 MiB (max 257 pages) to 512 MiB (max 2049 pages), 1000 is now within the allowed range, so this test no longer triggers the early num_private_input_pages &gt; max_pages bounds check in verify_with_options (prover/src/lib.rs ~1351). The test still passes (the verifier rejects for a different reason — count/Proof mismatch) but its name and the assertion message "Verifier must error on num_private_input_pages exceeding max" describe a path that is no longer exercised.

Evidence

Line 2821: hardcoded 1000. The companion continuation test at prover/src/continuation.rs:1551 correctly uses page::max_private_input_pages() + 1 which dynamically tracks the bound. The verify_with_options early-bounds check is at prover/src/lib.rs:1351.

Suggested fix

Replace 1000 with page::max_private_input_pages() + 1 (matching the pattern used in the continuation test at continuation.rs:1551) so the test continues to exercise the documented "exceeds max" code path regardless of future size changes.

AI-003: Test allocates ~512 MiB to exercise the oversized-input failure path
  • Status: confirmed
  • Severity: low
  • Location: executor/tests/flamegraph.rs:895
  • Found by: glm:openrouter/z-ai/glm-5.2, kimi:openrouter/moonshotai/kimi-k2.7-code
  • Verified by: deepseek-verifier:openrouter/deepseek/deepseek-v4-pro
  • Rejected by: -

Claim

The oversized-input test now allocates a Vec of MAX_PRIVATE_INPUT_SIZE + 1 bytes (512 MiB + 1) on the heap. Previously it used a hardcoded 64 MiB + 1. Tying it to the constant is good for maintainability, but it makes this single test reserve ~half a gigabyte of RAM, which can OOM or slow down constrained/parallel CI runners and cause intermittent failures as the constant grows.

Evidence

Line 895: let oversized_input = vec![0u8; executor::vm::memory::MAX_PRIVATE_INPUT_SIZE as usize + 1]; with MAX_PRIVATE_INPUT_SIZE now 512 * 1024 * 1024. The only way to trigger MemoryError::PrivateInputSizeExceeded in store_private_inputs is to pass a Vec whose len() exceeds the cap (memory.rs line 232), so the test must allocate > MAX bytes.

Suggested fix

Avoid tying the test allocation to the full MAX value. Either keep a small fixed oversized value (e.g. a few KiB) by temporarily lowering the threshold via a test-only constructor, or document/skip this test on memory-constrained runners. If the design truly requires >MAX bytes here, at minimum mark the test #[ignore] for heavy-alloc or guard it behind a feature so it doesn't run in every default cargo test on low-RAM CI.

AI-005: Stale 64 MiB reference in test comment
  • Status: confirmed
  • Severity: low
  • Location: prover/src/continuation.rs:1500
  • Found by: kimi:openrouter/moonshotai/kimi-k2.7-code, moonmath:zro/minimax-m3
  • Verified by: deepseek-verifier:openrouter/deepseek/deepseek-v4-pro
  • Rejected by: -

Claim

The comment in test_max_private_input_pages_is_tight still says the test avoids allocating a 64 MiB test input, but the constant was raised to 512 MiB.

Evidence

Line 1500 in prover/src/continuation.rs: // span so we don't allocate a 64 MiB test input). Line 1507 already says "(512 MiB + 4-byte prefix)" showing the constant was bumped, but this stale comment in the same test function was not updated to match.

Suggested fix

Update the comment to 512 MiB or rephrase it to refer to the MAX_PRIVATE_INPUT_SIZE constant so it cannot go stale again.

Reviewer Lanes

Lane Model Prompt Status Findings
glm openrouter/z-ai/glm-5.2 general success 1
kimi openrouter/moonshotai/kimi-k2.7-code general success 4
minimax minimax/MiniMax-M3 general error: opencode failed (provider/auth/runtime error) and no findings were submitted 0
moonmath zro/minimax-m3 general success 2
nemotron openrouter/nvidia/nemotron-3-ultra-550b-a55b general success 0

Verification Lanes

Lane Model Status Confirmed Rejected Uncertain
deepseek-verifier openrouter/deepseek/deepseek-v4-pro success 4 1 0

Native Codex and Claude reviews run separately and post their own comments. They are not included in this structured provenance report.

Discarded candidates (1) — rejected by the verifier
  • EF read_input does not clamp private-input length like get_private_input_slice does (syscalls/src/ef_io.rs:37, found by kimi:openrouter/moonshotai/kimi-k2.7-code) — The PR diff does not modify syscalls/src/ef_io.rs at all. The read_input function (lines 35-42) has always returned the raw length prefix without clamping — this behavior is identical regardless of whether MAX_PRIVATE_INPUT_SIZE is 64 MiB or 512 MiB. The mismatch between read_input and get_private_input_slice is a pre-existing design issue, not introduced or exposed by this constant-bump PR. The PR only changed the MAX_PRIVATE_INPUT_SIZE value in memory.rs and syscalls.rs, neither of which affects ef_io.rs behavior.

Raw lane outputs, candidates, final issues, and model metrics are uploaded as workflow artifacts.

Oppen added 2 commits July 17, 2026 13:41
Replace the hardcoded 1000-page bound with page::max_private_input_pages() + 1
so the "exceeds max" test keeps exercising the intended early-bounds-check
code path (1000 pages is now within range at 512 MiB). Also fix a stale
64 MiB reference in a neighboring test comment.
@Oppen

Oppen commented Jul 17, 2026

Copy link
Copy Markdown
Collaborator Author

Addressed the two cheap review findings (67bc664):

  • test_verify_rejects_num_private_input_pages_exceeds_max now uses page::max_private_input_pages() + 1 instead of a hardcoded 1000, which the 512 MiB bump had made a false-pass (1000 pages is now within bound).
  • Fixed the stale "64 MiB" comment in continuation.rs.

Known issue, tracked separately (not fixed in this PR): the reviewers are right that Memory::cells (a HashMap<u64, [u8;4]>) backing the private-input region doesn't scale to the new 512 MiB cap — measured peak RSS for a single store_private_inputs call at exactly MAX_PRIVATE_INPUT_SIZE is ~9.7 GB on this per-word hashmap design. We prototyped 5 alternative backing strategies (dense Vec, paged HashMap with Vec/inline-array/Box-array values) — all bring peak RSS down to ~1-2 GB and store time from ~2.2s to 30-90ms. Follow-up PR against main will land the chosen redesign. Merging this PR as-is since it unblocks other work and the clamp/wire-format correctness is unaffected — the memory-overhead issue is a resource-usage risk, not a soundness bug.

@Oppen

Oppen commented Jul 17, 2026

Copy link
Copy Markdown
Collaborator Author

Memory explosion will be fixed in a follow-up PR. For now it's rather theoretical.

@Oppen
Oppen enabled auto-merge July 17, 2026 19:31
@MauroToscano
MauroToscano disabled auto-merge July 17, 2026 19:32
@MauroToscano
MauroToscano merged commit 2baad17 into main Jul 17, 2026
13 checks passed
@MauroToscano
MauroToscano deleted the fix/max-private-input-size branch July 17, 2026 19:32
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants