From 381f43fe40d90b0a927d1300685ff8841bdce993 Mon Sep 17 00:00:00 2001 From: Mario Rugiero Date: Thu, 16 Jul 2026 17:53:28 -0300 Subject: [PATCH 1/2] fix(syscalls): raise the private-input clamp to 512 MiB 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. --- executor/src/vm/memory.rs | 7 +++---- executor/tests/flamegraph.rs | 2 +- prover/src/continuation.rs | 4 ++-- syscalls/src/syscalls.rs | 4 ++-- 4 files changed, 8 insertions(+), 9 deletions(-) diff --git a/executor/src/vm/memory.rs b/executor/src/vm/memory.rs index ea3b06c20..e1a269a01 100644 --- a/executor/src/vm/memory.rs +++ b/executor/src/vm/memory.rs @@ -42,10 +42,9 @@ pub type U64HashMap = HashMap; /// The COMMIT AIR concatenates calls via the running `x254` index, so this /// is enforced as a running-total budget rather than a per-call limit. pub const MAX_PUBLIC_OUTPUT_TOTAL_SIZE: u64 = 1024 * 1024; -/// Maximum size of the private input memory region (in bytes). 64 MiB so that a -/// whole `VmProof` can be passed as private input to a verifier guest (naive -/// recursion). -pub const MAX_PRIVATE_INPUT_SIZE: u64 = 64 * 1024 * 1024; +/// Maximum size of the private input memory region (in bytes). 512 MiB so a +/// real proof (e.g. a continuation bundle) fits as private input. +pub const MAX_PRIVATE_INPUT_SIZE: u64 = 512 * 1024 * 1024; /// Fixed high address where private input is mapped. Guest programs can read /// directly from this address (ZisK-style memory-mapped input). /// Layout: 4-byte LE length prefix at `PRIVATE_INPUT_START_INDEX`, then data at +4. diff --git a/executor/tests/flamegraph.rs b/executor/tests/flamegraph.rs index b0c5b7a24..f5735c226 100644 --- a/executor/tests/flamegraph.rs +++ b/executor/tests/flamegraph.rs @@ -892,7 +892,7 @@ fn test_run_with_flamegraph_returns_generator_on_executor_new_failure() { // even on failure. let elf_bytes = std::fs::read("./program_artifacts/rust/add.elf").unwrap(); let program = executor::elf::Elf::load(&elf_bytes).unwrap(); - let oversized_input = vec![0u8; 64 * 1024 * 1024 + 1]; + let oversized_input = vec![0u8; executor::vm::memory::MAX_PRIVATE_INPUT_SIZE as usize + 1]; let (generator, result) = executor::flamegraph::run_with_flamegraph( &elf_bytes, diff --git a/prover/src/continuation.rs b/prover/src/continuation.rs index 2e5c56a8b..6815c1fdd 100644 --- a/prover/src/continuation.rs +++ b/prover/src/continuation.rs @@ -1504,9 +1504,9 @@ mod tests { let page_size = page::DEFAULT_PAGE_SIZE; let max = page::max_private_input_pages(); - // (64 MiB + 4-byte prefix) / 256 KiB page = 257 pages (256 full data pages plus + // (512 MiB + 4-byte prefix) / 256 KiB page = 2049 pages (2048 full data pages plus // the one page the length prefix spills into). Pinned so a size/page change is caught. - assert_eq!(max, 257); + assert_eq!(max, 2049); // No slack: an honest MAX-size input needs the whole last page (the bound is not // padded), and never overflows into an extra one. diff --git a/syscalls/src/syscalls.rs b/syscalls/src/syscalls.rs index ad9947855..7165dff81 100644 --- a/syscalls/src/syscalls.rs +++ b/syscalls/src/syscalls.rs @@ -8,14 +8,14 @@ use core::arch::asm; #[cfg(target_arch = "riscv64")] pub const PRIVATE_INPUT_START: usize = 0xFF000000; -/// Maximum private-input length the guest will read, in bytes (64 MiB). +/// Maximum private-input length the guest will read, in bytes (512 MiB). /// The host caps stored input at this size in `Memory::store_private_inputs`, /// so an honest length prefix is always `<=` this bound; a larger value can only /// come from a malformed or forged prefix. The reader clamps to this cap so a /// bogus length can never make the guest fabricate an arbitrarily long slice. /// Must match `executor::vm::memory::MAX_PRIVATE_INPUT_SIZE`. #[cfg(target_arch = "riscv64")] -const MAX_PRIVATE_INPUT_SIZE: usize = 64 * 1024 * 1024; +const MAX_PRIVATE_INPUT_SIZE: usize = 512 * 1024 * 1024; #[cfg(target_arch = "riscv64")] pub enum SyscallNumbers { From 67bc664a6db3655a62ccce093849a7342b1ef9df Mon Sep 17 00:00:00 2001 From: Mario Rugiero Date: Fri, 17 Jul 2026 16:29:00 -0300 Subject: [PATCH 2/2] test(prover): tighten private-input bound tests after the 512 MiB bump 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. --- prover/src/continuation.rs | 2 +- prover/src/tests/prove_elfs_tests.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/prover/src/continuation.rs b/prover/src/continuation.rs index 6815c1fdd..d0e123f9d 100644 --- a/prover/src/continuation.rs +++ b/prover/src/continuation.rs @@ -1497,7 +1497,7 @@ mod tests { // The deserialized-count bound is the tight honest max: exactly the pages a MAX-size // input occupies, with no slack. Pin the value and the tightness (checked via the byte - // span so we don't allocate a 64 MiB test input). + // span so we don't allocate a 512 MiB test input). #[test] fn test_max_private_input_pages_is_tight() { use executor::vm::memory::{MAX_PRIVATE_INPUT_SIZE, PRIVATE_INPUT_LENGTH_PREFIX_BYTES}; diff --git a/prover/src/tests/prove_elfs_tests.rs b/prover/src/tests/prove_elfs_tests.rs index 527c092c5..864e4e3f9 100644 --- a/prover/src/tests/prove_elfs_tests.rs +++ b/prover/src/tests/prove_elfs_tests.rs @@ -2818,7 +2818,7 @@ fn test_verify_rejects_num_private_input_pages_exceeds_max() { let vm_proof = crate::prove_with_inputs(&elf_bytes, &input).expect("prove should succeed"); let tampered = crate::VmProof { - num_private_input_pages: 1000, + num_private_input_pages: crate::tables::page::max_private_input_pages() + 1, ..vm_proof };