Precompile Summary

PropertyValue
Address0x07
NameBN256MUL (also called ECMUL, alt_bn128 scalar multiplication)
Gas6,000 (post-EIP-1108, Istanbul). Was 40,000 pre-Istanbul.
Input96 bytes: point x (32 bytes), point y (32 bytes), scalar s (32 bytes)
Output64 bytes: resulting point (x', y') = s * (x, y) on the curve
BehaviorPerforms scalar multiplication of a point on the alt_bn128 (BN254) curve by a scalar. The input point must lie on the curve or be the point at infinity; otherwise the call fails (empty return). Introduced in Byzantium (EIP-196). Used for zkSNARK verification, BLS-style operations built on the pairing curve, and Pedersen commitments.

Threat Surface

BN256MUL sits on the hot path for many zero-knowledge and cryptographic protocols: any verifier that multiplies curve points by scalars before pairing checks, Pedersen-style openings, or similar gadgets depends on this precompile. Its threat surface combines correctness under adversarial inputs, cross-client consensus, and how Solidity and low-level callers interpret failure.

Unlike pure hash precompiles, scalar multiplication amplifies small errors in point handling. If an implementation accepts a point that is not on the curve (or validates the wrong algebraic property, such as subgroup checks without full on-curve verification), the computed s * P can diverge from the specification and from other clients. That divergence is especially dangerous when the output feeds BN256PAIRING (0x08) or other steps that assume points are honest curve elements: an off-curve intermediate can make verification accept or reject proofs incorrectly relative to the intended cryptography.

At the smart-contract layer, the EVM still charges fixed gas (6,000 post-Istanbul) regardless of the scalar’s Hamming weight, while real wall-clock time in the client may vary with the scalar’s bit pattern—a narrow timing side channel that rarely matters for on-chain logic but can matter for co-located infrastructure. More practically, contracts that ignore CALL success or return data length risk reusing stale memory as if it were a fresh curve point, which is catastrophic when that value is later used in pairing-based soundness arguments.

Smart Contract Threats

T1: Point Validation Bypass — CVE-2025-30147 (Critical)

A Besu vulnerability in the gnark-crypto path used for BN256 curve precompiles (including BN256MUL) showed that the implementation did not correctly ensure inputs were on the alt_bn128 curve, relying on checks that were insufficient (e.g., subgroup-oriented validation without proper on-curve verification, analogous to the BN256ADD issue). Scalar multiplication of an off-curve point yields results that do not match a correct client: the “error” is not a clean revert at the EVM boundary but wrong curve points that can flow into downstream verification.

  • Amplification. Addition mis-handles one bad point; scalar multiplication mixes that point through a full double-and-add style schedule, so incorrect assumptions about P produce outputs that are wrong in ways that are hard to predict from local tests alone.
  • Consensus and cross-chain risk. Any client that accepts points another client rejects (or vice versa) risks consensus failure; in the CVE window, the practical impact was divergent precompile output vs. correct implementations for malformed-but-subgroup-passing inputs.
  • zkSNARK verifier blast radius. Verifiers rarely call BN256MUL in isolation; they combine it with BN256ADD and BN256PAIRING. A single bad intermediate breaks the algebraic relations the proof system assumes.

Why it matters: This class of bug is not “fails safe”; it can make valid proofs fail or invalid proofs appear valid depending on how the contract combines results, undermining the security story of the entire proof gadget.

T2: Timing Side-Channel Concerns (Low)

Scalar multiplication algorithms often exhibit data-dependent execution time (e.g., number of additions depends on which bits of the scalar are set). EVM gas for BN256MUL is fixed at 6,000 (post-Istanbul), so on-chain economic cost does not leak the scalar. However, node-local wall-clock time may still correlate with the scalar value.

  • Exploitability in practice. Exploiting this from a smart contract is extremely limited: miners/validators do not typically expose fine-grained timing of precompile execution to arbitrary callers in a reliable way. Risk is mostly relevant to shared infrastructure, benchmarking, or co-located workloads—not to typical Solidity business logic.
  • Still worth documenting for completeness when threat-modeling privacy-critical systems that also run off-chain provers next to validating nodes.

Why it matters: Low severity for EVM contracts, but it is a real property of many implementations and belongs in a complete precompile threat model.

T3: Unchecked Return Value / Stale Memory (Medium)

The same pattern as BN256ADD: if a contract uses assembly, inline Yul, or low-level call to 0x07 and does not assert success and 64-byte return data, Solidity may leave previous memory contents in place. The caller can then treat garbage or old (x, y) as the result of s * P.

  • Downstream pairing checks. Stale coordinates are not random noise in a well-designed verifier; they are structured field elements that can interact badly with pairing equations and library assumptions.
  • Harder to spot than a revert. High-level staticcall wrappers that ignore the boolean return are a recurring audit theme; scalar multiplication makes the consequences worse because the bad values feed multi-step protocols.

Why it matters: A missed success check turns a deterministic cryptographic primitive into an uninitialized-memory bug with proof-system impact.

Protocol-Level Threats

P1: Gas repricing (EIP-1108, Istanbul)

EIP-1108 reduced BN256MUL gas from 40,000 to 6,000 (~85% drop), making zkSNARK verification and related gadgets roughly 6.7× cheaper in the curve-arithmetic portion of typical verifiers. This unlocked privacy protocols and L2 systems that were previously gas-prohibitive.

  • Tradeoff. Cheaper verification increases on-chain attack surface density: more contracts can afford full verifiers, so implementation bugs (client or contract) affect more value and more protocols.

P2: Library migration and insufficient validation coverage (CVE-2025-30147)

CVE-2025-30147 illustrates that switching the underlying library (e.g., to gnark-crypto) can introduce subtle validation gaps not caught by legacy test vectors, which often assume honest or only mildly malformed inputs. Subgroup checks, encoding conventions, and point-at-infinity handling differ in subtle ways across libraries.

  • Lesson. Consensus-critical cryptography needs negative testing and cross-client differential fuzzing, especially when replacing implementations.

P3: Fixed gas vs. data-dependent real cost

The 6,000 gas is independent of the scalar value. Worst-case scalars (many non-zero bits) induce more group operations than sparse scalars in typical implementations, so worst-case work can be underpriced relative to best-case scalars at the protocol level.

  • Impact. Primarily an economic / DoS margin concern for block producers (more work per gas in pathological cases), not a direct soundness bug. It interacts with global block gas limits and scheduling but does not by itself break curve math.

Edge Cases

Edge CaseBehaviorSecurity Implication
Point at infinity represented as (0, 0)(0, 0) * s → (0, 0) for any scalarCallers must treat (0,0) as the designated infinity encoding per spec; mishandling breaks verifier logic.
P * 0Yields the point at infinity (0, 0)Scripts must not assume a non-infinity output when scalar is zero.
P * 1Returns PIdentity case; useful for testing but easy to confuse with unchecked-return bugs if success is not checked.
P * n where n is the curve orderYields (0, 0) (infinity)Consistent with group law; verifiers must expect infinity outputs in honest proofs.
Scalar curve orderReduced modulo the curve orderCallers must not assume scalars are already canonically reduced unless they enforce it off-chain.
Input shorter than 96 bytesRight-padded with zeros (as for other precompiles)Truncated or padded inputs can produce unexpected points or scalars; treat as untrusted encoding.
Point not on curve (and not valid infinity encoding)Call fails: empty return dataIf success is not checked, stale memory may be used (T3).

Real-World Exploits

CVE-2025-30147 (Critical) — Besu / gnark-crypto validation gap

Root cause: Incorrect or insufficient on-curve validation in the gnark-crypto-based implementation of BN256 precompiles (shared context with BN256ADD); off-curve points could be processed in a way that diverged from correct clients.

BN256MUL’s role: Scalar multiplication amplifies mishandled points and feeds pairing-heavy verifiers; impact is not isolated to a single precompile call.

Impact: Consensus-threatening client divergence and broken cryptographic assumptions for zkSNARK and related on-chain verifiers relying on 0x06 / 0x07 / 0x08.

Note: There are no well-known exploits limited only to BN256MUL in isolation; practical fallout is systemic to any gadget that trusts these precompiles.

Attack Scenarios

Scenario A: BN256MUL without checking success — stale memory

// VULNERABLE PATTERN (illustrative Yul-style usage)
pragma solidity ^0.8.0;
 
contract UnsafeBn256Mul {
    function mulAndUse(
        bytes32 x,
        bytes32 y,
        bytes32 s
    ) external view returns (bytes32 outX) {
        bytes memory input = abi.encodePacked(x, y, s);
        bytes memory output = new bytes(64);
 
        assembly {
            let success := staticcall(
                gas(),
                0x07,
                add(input, 32),
                96,
                add(output, 32),
                64
            )
            // VULNERABLE: success ignored; if staticcall fails, `output` may be
            // all zeroes or retain prior memory contents depending on compiler/runtime.
            outX := mload(add(output, 32))
        }
 
        // Attacker triggers failure (e.g., off-curve point) and relies on
        // `outX` still being used in a pairing check elsewhere.
    }
}

Scenario B: Pedersen-style check that assumes success without on-curve proofs

// CONCEPTUAL: commitment verification that trusts BN256MUL output blindly
pragma solidity ^0.8.0;
 
contract RiskyPedersenCheck {
    // G, H assumed fixed generators on alt_bn128 (illustrative only)
    function verifyOpening(
        bytes32 Px,
        bytes32 Py,
        bytes32 s,
        bytes32 expectedX,
        bytes32 expectedY
    ) external view returns (bool ok) {
        bytes memory input = abi.encodePacked(Px, Py, s);
        bytes memory result = new bytes(64);
 
        (bool success,) = address(0x07).staticcall(input);
        // VULNERABLE: does not require(success) or result.length == 64
        assembly {
            returndatacopy(add(result, 32), 0, returndatasize())
        }
 
        bytes32 rx;
        bytes32 ry;
        assembly {
            rx := mload(add(result, 32))
            ry := mload(add(result, 64))
        }
 
        // If the call failed, rx/ry may be unrelated to s * P — yet compared
        // to expected values or fed into a pairing-based relation.
        ok = (rx == expectedX && ry == expectedY);
    }
}

Mitigations

ThreatMitigationImplementation
T1: Point validation / client bugsRun patched consensus clients; monitor advisoriesTreat CVE-2025-30147-class issues as node ops fixes; contracts cannot patch the client.
T1: Protocol designAvoid trusting single-client behavior on malformed inputsFavor standardized encoding, explicit rejection of invalid points at the contract boundary where possible, and cross-client testing before mainnet deployment.
T2: TimingIsolate sensitive workloadsDo not rely on secret scalars in environments where micro-architectural timing attacks are in scope; keep secrets off the hot path next to validators if threat model requires it.
T3: Unchecked returnAlways check success and 64-byte outputrequire(success && returndatasize() == 64); then copy return data; in Solidity 0.8+, prefer typed wrappers that revert on failure.
T3: Memory hygieneZero or overwrite buffers after failed callsWhen using assembly, explicitly mstore zeros on failure paths before branching.
GeneralUse audited libraries for bn128 gadgetsLibraries that wrap 0x06 / 0x07 / 0x08 with consistent error handling reduce foot-guns.

Compiler/EIP-Based Protections

  • EIP-196 (Byzantium): Defines alt_bn128 precompiles including BN256MUL; canonical reference for intended curve and encoding behavior.
  • EIP-1108 (Istanbul): Reprices BN256MUL gas to 6,000; dramatically lowers cost of honest verifiers but increases deployed surface area for curve arithmetic.
  • Solidity / high-level patterns: Prefer abstractions that revert on failed staticcall to 0x07 rather than raw assembly without checks; the compiler does not auto-mitigate T3 in hand-written Yul.

Severity Summary

Threat IDCategorySeverityLikelihoodNotes
T1Smart Contract / ClientCriticalLow (patched clients) / High (during active bug window)CVE-2025-30147 class; wrong curve math affecting verifiers and consensus.
T2Smart Contract / InfraLowLowData-dependent timing; minimal typical on-chain exploitability.
T3Smart ContractMediumMediumUnchecked staticcall and return size; stale memory in pairing pipelines.
P1ProtocolN/A (design tradeoff)N/AEIP-1108 enabled zkSNARK scaling; increases density of verifier logic.
P2Protocol / EngineeringHighMediumLibrary swaps need differential testing; CVE-2025-30147 precedent.
P3Protocol (economic)LowN/AFixed gas vs. variable work; node-load / scheduling margin, not soundness.
PrecompileRelationship
BN256ADD (0x06)Point addition on the same curve; verifiers almost always combine add and mul before pairings.
BN256PAIRING (0x08)Pairing check consumes G1/G2 points produced by upstream add/mul steps; errors in 0x07 break pairing soundness.
ECRECOVER (0x01)Different curve (secp256k1), but same lesson: silent failure modes and caller-side checks dominate security.
MODEXP (0x05)Used in RSA-style and modular arithmetic gadgets; often appears alongside heavy crypto in verification contracts.