7Block Labs
Blockchain Security

ByAUJay

Summary: In January 2025, Succinct’s SP1 team published concrete security guidance after patching three vulnerabilities and freezing legacy verifier routers. This checklist distills what changed and exactly how to audit zkVM “guest” programs so your proofs remain sound, verifiable on-chain, and production-ready.

Auditing zkVM Guest Programs: A Checklist Inspired by 2025’s SP1 Security Guidance

Decision-makers increasingly rely on zkVMs to underpin rollups, coprocessors, and cross-chain bridges. The catch: zkVMs only prove the correctness of your guest program’s execution; they don’t make insecure programs safe. In January 2025, Succinct published an SP1 Security Update with concrete mitigations and upgrade instructions. We turned those lessons into a practical, auditable checklist you can apply before your next launch. (blog.succinct.xyz)

Below you’ll find:

  • What changed in 2025 for SP1 security (and why it matters).
  • A step-by-step audit checklist covering toolchains, public values, syscalls, precompiles, serialization, determinism, recursion, on-chain verification, and more.
  • Hands-on examples and “red flag” patterns to catch in code review.
  • When to flip on SP1-2FA (TEE-backed two-factor proving) to raise your assurance bar even further.

1) What changed in 2025: the SP1 security context

In Jan 2025, SP1 Turbo (v4.x) shipped with fixes for:

  • Missing validation of chip ordering in the STARK verifier (soundness issue, affected direct verification of core shard proofs).
  • Missing call(s) to assert that a recursive proof represents a complete execution (is_complete), enabling partial executions to pass as full ones in some contexts.
  • A Plonky3 batching flaw where evaluation claims were not absorbed into Fiat–Shamir before sampling combination coefficients (letting incorrect polynomial evaluations pass). (blog.succinct.xyz)

Succinct recommended immediate upgrades to Turbo and froze routers to mainnet verifier contracts for older versions. Their notice also clarified a developer footgun: don’t directly call HALT or other syscalls—use the SP1 entrypoint so COMMIT events are enforced and public values are constrained as intended. (blog.succinct.xyz)

A separate researcher disclosure detailed how two issues in the SP1 SDK v3.4.0 could be combined to forge proofs (e.g., unconstrained committed_value_digest if COMMIT never fires due to early HALT, and an unchecked recursion condition). The Turbo release addressed these vectors. (blog.lambdaclass.com)

Takeaway for leaders: a hardened zkVM still requires a hardened guest program and a disciplined development lifecycle. zkVM vendors themselves state that many security issues originate in guest code and surrounding contracts—not in the proving tech. (dev.risczero.com)


2) Threat model refresher: what the proof actually covers

  • The proof vouches for the guest program’s execution, not the host orchestrator. The host can supply any inputs; only the guest’s logic and committed outputs are cryptographically bound. Treat host code as untrusted orchestration. (blog.sigmaprime.io)
  • SP1’s security model requires your compiled program to be memory-safe, non-malicious, and properly constrained (including safe precompile usage). The model also assumes 0 is not a valid program counter in the compiled binary. (docs.succinct.xyz)

3) The 7Block Labs SP1 Guest Security Checklist

Adopt these items as “must pass” gates in PRs and pre-release sign-off.

A. Toolchain, versioning, and proof types

  • Pin to SP1 Turbo (v4.x or later). Older versions (V2/V3) were sunset on the Succinct Prover Network starting February 15, 2025; ensure your build/CI, scripts, and infrastructure use Turbo. (blog.succinct.xyz)
  • Choose proof type consciously: core vs compressed vs Groth16 vs PLONK. Remember:
    • Groth16/PLONK require trusted setups; Succinct uses a circuit-specific ceremony for Groth16 and Aztec Ignition for PLONK. Groth16 is faster but carries higher ceremony-specific trust. (docs.succinct.xyz)
    • Use approved prover versions for critical apps and apply security patches promptly. (docs.succinct.xyz)

B. Reproducible, attestable builds and vkey checks

  • Development builds aren’t necessarily reproducible. For production, use SP1’s reproducible build flow to produce a deterministic ELF. (docs.succinct.xyz)
  • Verify the verification key (vkey) derived from your ELF matches the on-chain verifier’s expectations. SP1 projects (e.g., Blobstream) document a reproducible build-and-verify flow: build with cargo prove (Docker-tagged), then run the vkey tool and compare against the contract. Make this a CI step. (succinctlabs.github.io)
  • Background: Reproducible builds give verifiable source-to-binary lineage—an industry baseline for supply-chain trust. (glossary.openssf.org)

C. Public values (I/O) discipline: commit the right things, in the right order

  • Only data you commit inside the guest with sp1_zkvm::io::commit or commit_slice becomes public to verifiers. The verifier reads public values in exactly the order you commit them—treat that order as part of your ABI. (docs.succinct.xyz)
  • Anti-pattern: early HALT or returning before deferred COMMIT events flush. Use the standard program entrypoint and never call HALT/syscalls directly. This avoids “unconstrained public value digest” issues that Turbo explicitly closed at the recursion layer, and prevents developer-induced footguns. (blog.succinct.xyz)

Example (bad):

use sp1_zkvm::syscalls::syscall_halt;
fn main() {
    // ... compute outputs ...
    syscall_halt(0); // dangerous: bypasses normal commit/exit semantics
}

Example (good):

fn main() {
    let out = compute();
    sp1_zkvm::io::commit(&out);
    // return normally; let the SP1 entrypoint handle HALT safely
}

D. Syscalls and “unconstrained” code: minimize and justify

  • Prefer patched crates and safe wrappers over direct syscalls. SP1 exposes dozens of syscalls (e.g., SHA-256 extend/compress, secp256r1 ops, write/read), but interacting directly carries footgun risk. Treat it like unsafe Rust—only with clear necessity and tests. (docs.rs)
  • The unconstrained macro runs code outside the VM’s constraints. Use it only to precompute hints that don’t affect committed outputs, and never to derive values you will later commit without re-verification. Document every use with rationale and tests. (docs.rs)

Red flag pattern:

let y = sp1_lib::unconstrained!({
  // network call / randomness / anything host-like (don’t do this in guest)
  // or critical business logic
});
// ... later ...
sp1_zkvm::io::commit(&y); // committing values derived *outside* constraints

E. Serialization and input handling: bound everything, avoid cycle-heavy formats

  • Don’t pay cycles for generic serializers by default. SP1’s performance guidance recommends zero-copy I/O paths: read_slice/read_vec and rkyv-like formats, and commit_slice for bytes. Bound input lengths and validate upfront. (docs.succinct.xyz)
  • If you must use serde types, do so deliberately and profile. There’s historical context around bincode inefficiency for zkVM I/O; the team introduced better-aligned approaches. (github.com)

F. Precompiles: use patched crates, not raw syscalls

  • SP1 Turbo adds new precompiles (e.g., secp256r1, bigint for RSA) and ships a growing set of precompile-backed “patched crates” that redirect standard library calls (sha2, sha3, etc.) to optimized syscalls. Prefer the patched crates over calling precompile syscalls by hand. (blog.succinct.xyz)
  • Precompile specification: under the hood they’re custom STARK tables called through RISC-V ecall with unique syscall numbers. In audits, verify you’re on vetted versions of patched crates and that inputs are validated. (docs.succinct.xyz)

G. Recursion correctness: assert “complete execution” and test for early-exit shenanigans

  • Turbo patches missing calls that assert recursive proofs are complete executions. Still, add tests to ensure your program cannot bypass essential checks (e.g., by returning early before all commitments happen). This is especially important when you rely on compressed proofs or on-chain gateways. (blog.succinct.xyz)

H. On-chain verification gotchas: hash functions and gateways

  • SP1’s Solidity verifier currently hardcodes SHA-256 for public value hashing. If you enable the blake3 feature in sp1-zkvm, your proofs won’t verify against the default on-chain verifier. Don’t turn this on in production unless you control the verifier. (docs.succinct.xyz)
  • If you migrated to Turbo post-incident, confirm your verifier gateway/router addresses and vkey roots point to the Turbo-safe deployments Succinct provided. This was part of the January 27, 2025 response. (blog.succinct.xyz)

I. Determinism: no ambient entropy, clocks, or host assumptions

  • Guest code runs in a VM without OS/network. Anything “ambient” must be modeled as input, read via io::read, and validated. Don’t depend on time, randomness, or host-side state unless explicitly provided as inputs and verified by the guest. (blog.sigmaprime.io)

J. Memory safety and undefined behavior

  • SP1’s model assumes a non-malicious, memory-safe program and enforces reserved memory constraints in the AIR so registers’ reserved regions are protected. Still, UB in guest Rust can be “proven” as correct—that’s on you to prevent via safe Rust, Miri, and fuzzing. (blog.succinct.xyz)
  • Don’t rely on platform-specific layout tricks. Pin repr(C) where ABI-stability matters; include test vectors that roundtrip inputs/outputs across versions.

K. Proving infra and upgrades

  • For non-trivial workloads, prefer the (patched) Succinct Prover Network; it can parallelize proofs across a cluster for latency while keeping costs steady—and it’s where security features like SP1-2FA land first. (blog.succinct.xyz)
  • Subscribe monitors to Succinct advisories/blog. After January 2025, several performance/security improvements (Turbo, then Hypercube) shipped; staying current is part of your risk management. (blog.succinct.xyz)

4) Hands-on example: auditing a signature-verification guest

Scenario: your guest reads n messages and ECDSA-secp256r1 signatures, verifies them, and outputs a Merkle root of valid message hashes.

Checklist walk-through (with fixes):

  1. Inputs are bounded and zero-copy.
#[derive(serde::Deserialize)]
struct SigInput { /* msg, sig, pk */ }

// Better: read bytes, then parse with a zero-copy schema.
let raw = sp1_zkvm::io::read_vec();           // zero-copy
// parse_raw() should bound lengths and validate structure.
// Avoid variable-sized allocations without caps.
let items = parse_raw(&raw).expect("invalid input");
  • Why: bound the witness size and avoid cycle-heavy generic (de)serialization. (docs.succinct.xyz)
  1. Use patched crates for hashing; avoid raw syscalls.
# Cargo.toml (guest)
# Routes sha2 to SP1 precompiles; versions must match your SP1 release
sha2-v0-10-9 = { git = "https://github.com/sp1-patches/RustCrypto-hashes", package = "sha2", tag = "patch-sha2-0.10.9-sp1-4.0.0" }
  • Why: audited speedups with correct constraints; avoid hand-rolled syscall glue. (docs.succinct.xyz)
  1. Verify signatures via precompiles, not in “unconstrained.”
use p256::ecdsa::{VerifyingKey, Signature}; // routed via patched crates/precompiles when available
let vk = VerifyingKey::from_sec1_bytes(&item.pk)?;
let sig = Signature::from_der(&item.sig)?;
vk.verify(item.msg, &sig)?; // executed with SP1 acceleration under the hood
  • Why: unconstrained code is outside the VM; never derive committed values this way. (docs.rs)
  1. Commit outputs in deterministic order and return normally.
let root = compute_merkle_root(valid_msgs);
sp1_zkvm::io::commit_slice(root.as_bytes()); // public
// return; SP1 handles COMMIT/HALT correctly
  • Why: the verifier reads public values in commit order; you must not early-HALT. (docs.succinct.xyz)
  1. Enable the right proof type and confirm on-chain compatibility:
  • Use SHA-256 public value hashing (default). Don’t enable blake3 feature if you need the standard Solidity verifier. (docs.succinct.xyz)
  1. Reproducible build and vkey check in CI:
  • Build the ELF with cargo prove (Docker-tagged), then run the vkey tool and compare to the contract’s stored vkey root before deployment. Fail CI if mismatched. (succinctlabs.github.io)

5) Emerging best practices from SP1 Turbo and beyond

  • New precompiles (secp256r1, bigint for RSA) can drastically reduce cycles—great for wallet, identity, and enterprise workloads that lean on non-secp256k1 curves. Align your cryptography choices with available precompiles and patched crates. (blog.succinct.xyz)
  • Performance tuning: thin/fat LTO, codegen-units=1, avoid unnecessary copies; SP1 docs outline practical compiler flags and I/O optimizations. Trim serde usage when possible. (docs.succinct.xyz)
  • Keep an eye on SP1 Hypercube and formal verification efforts (e.g., formally verified RISC‑V constraints). These shift not just performance but the assurance story for zkVM deployment. (blog.succinct.xyz)

6) When to enable SP1-2FA (TEE-backed two-factor proving)

SP1-2FA runs your SP1 executor inside an AWS Nitro enclave and requires the enclave’s attested outputs to match the ZK proof, giving you defense-in-depth: an attacker must break both ZK and hardware isolation to subvert results. Integration is a single line when you use the Prover Network; it’s in private beta with security audit coverage as of 2025. Use it for:

  • High-value transactions or bridges where proof forgery would be catastrophic.
  • Regulated enterprise deployments that mandate hardware-rooted attestations.
  • Interim assurance while formal verification coverage expands. (blog.succinct.xyz)

Important: SP1-2FA guards against proving-system flaws, not application bugs. You still need the guest audit above. (blog.succinct.xyz)


7) Vendor questions to ask before greenlighting a release

  • What exact SP1 version, proof type, and patched crate tags are pinned? Show lockfiles and tags.
  • Demonstrate a reproducible build + vkey match against the on-chain verifier.
  • List all places using unconstrained or direct syscalls; provide tests proving they don’t affect committed outputs.
  • Show input bounds and fuzz/negative tests for deserialization.
  • If using non-default public value hashing, show verifier compatibility and contracts.
  • If using the Prover Network, is SP1-2FA enabled? What’s the attestation verification path?

8) Quick red flags to stop a merge

  • Direct syscall invocations (e.g., HALT, write/read) without strong justification and tests. (blog.succinct.xyz)
  • Any unconstrained block that produces values later committed. (docs.rs)
  • Unbounded read_vec usage with unchecked lengths.
  • Enabling blake3 public value hashing while targeting the standard Solidity verifier. (docs.succinct.xyz)
  • Building on pre‑Turbo branches or ignoring the January 2025 router freeze guidance. (blog.succinct.xyz)

9) Action plan you can run this week

  • Migrate to SP1 Turbo if you haven’t; re-run proofs and on-chain verifiers in staging. (blog.succinct.xyz)
  • Add CI: reproducible build, vkey check, cargo-audit on sp1_* crates, and deny “unconstrained/syscall” patterns without waivers.
  • Switch to patched crates for crypto primitives and adopt zero-copy I/O paths. (docs.succinct.xyz)
  • Stage SP1-2FA for your highest-risk proving jobs. (blog.succinct.xyz)

Closing

Auditing zkVM guest programs is not optional—proofs are only as strong as the code they bind. The 2025 SP1 security updates clarified where guest programs can go wrong and how to build guardrails in your pipeline. If you adopt this checklist, you’ll ship faster with higher assurance—and fewer surprises at audit time.

If you’d like 7Block Labs to run this checklist against your SP1 codebase, we’ll deliver a gap analysis with concrete fixes, a migration plan to Turbo or Hypercube-compatible flows, and CI guardrails you can keep running after we leave.


Sources and further reading

  • SP1 Security Update (Jan 27, 2025): vulnerabilities fixed in Turbo; upgrade and router freeze guidance. (blog.succinct.xyz)
  • LambdaClass disclosure: combining two issues in sp1‑sdk v3.4.0 to forge proofs; patched in Turbo. (blog.lambdaclass.com)
  • SP1 docs: inputs/outputs, compilation, optimization, patched crates, precompile spec, security model, advanced features (Blake3 caveat). (docs.succinct.xyz)
  • SP1 Turbo release: new precompiles (secp256r1, RSA/bigint), cluster proving benchmarks. (blog.succinct.xyz)
  • RISC Zero security model: guest program security remains your responsibility; helpful for threat-model alignment. (dev.risczero.com)
  • Reproducible builds background and SP1 example (Blobstream): how and why to verify vkeys. (glossary.openssf.org)
  • SP1‑2FA announcement: TEE-backed second factor on the Prover Network (private beta). (blog.succinct.xyz)

Like what you're reading? Let's build together.

Get a free 30‑minute consultation with our engineering team.

Related Posts

7BlockLabs

Full-stack blockchain product studio: DeFi, dApps, audits, integrations.

7Block Labs is a trading name of JAYANTH TECHNOLOGIES LIMITED.

Registered in England and Wales (Company No. 16589283).

Registered Office address: Office 13536, 182-184 High Street North, East Ham, London, E6 2JA.

© 2025 7BlockLabs. All rights reserved.