Skip to content

fix(sigma-ser,ergo-nipopow,ergo-lib): don't size parse buffers by untrusted u32 prefixes - #947

Open
Kshot3000 wants to merge 1 commit into
ergoplatform:developfrom
Kshot3000:fix/parse-prealloc-dos-scorex
Open

Kshot3000 wants to merge 1 commit into
ergoplatform:developfrom
Kshot3000:fix/parse-prealloc-dos-scorex

Conversation

@Kshot3000

Copy link
Copy Markdown
Contributor

Fixes #946. Sibling of #945 (which fixed the same bug class in ergotree-ir's Sigma paths for #944); this covers the Scorex serialization layer and the crates built on it, which that audit didn't reach.

The gap

Four parse paths sized a buffer by an untrusted u32 prefix before reading a single payload byte, so a few input bytes aborted the process with an allocation failure instead of returning a parse error (all reproduced on develop under a 2 GB address-space limit):

  • Vec<T>::scorex_parse — VLQ u32::MAX count → 17 GB allocation (for u32 items), SIGABRT
  • NipopowProof::scorex_parse prefix count u32::MAX → ~1.58 TB allocation, SIGABRT
  • PoPowHeader::scorex_parse header size u32::MAX → 4 GB allocation, SIGABRT
  • ReducedTransaction::sigma_parse tx length u32::MAX → 4 GB allocation, SIGABRT

NiPoPoW proofs arrive from peers, and a reduced transaction (EIP-19) exists to be transferred and parsed by another party (e.g. a cold wallet) — both should fail with a parse error, not abort.

The fix (same pattern as #945)

  • sigma-ser: Vec<T>::scorex_parse caps the pre-allocation at 1024 elements (the buffer still grows to the real count as items parse). New pub helper scorex_parse_bytes_exact reads exactly the claimed length, growing the buffer only as bytes arrive and erroring exactly like read_exact (Io("failed to fill whole buffer")) on a short stream.
  • ergo-nipopow: prefix and suffix-tail pre-allocations capped the same way; the PoPowHeader header and interlinks-proof buffers now go through scorex_parse_bytes_exact.
  • ergo-lib: ReducedTransaction's embedded-transaction buffer goes through the same helper; the helper's Io error is mapped back to SigmaParsingError::Io, so a short stream fails with exactly the error read_exact produced before (pinned by the new test).

Deliberate non-changes

The rest of the workspace's allocation sites were audited and are not affected: u8/u16 count prefixes (context extension, box tokens, tuples, collection/sigma-boolean items, ProofBytes, peer features, header fields), the transaction token-id count (bound-checked against MAX_OUTPUTS_COUNT * MAX_TOKENS_COUNT before allocating — the codebase's own precedent), and BigInt256/UnsignedBigInt (length checked ≤ 32 first). BatchMerkleProof collects its u32-length indices/proofs without pre-allocating by the claimed length, so a huge length already fails on the first read — verified empirically, left alone. The PoPowHeader interlinks collection is the same safe pattern.

Tests

  • New regression tests for every fixed path (huge prefix → parse error, under the same allocation cap that aborted the old code), plus exact/short/empty pins for scorex_parse_bytes_exact.
  • cargo test: sigma-ser 56, ergo-nipopow 5, ergo-lib (--all-features) 122+1, ergo-merkle-tree and ergo-chain-types green, ergotree-ir + ergotree-interpreter green. Clippy -D warnings clean on the touched crates; cargo fmt --check clean.

Built by @Kshot9000 — donations (Ergo): 9fcM5RWnAjmP4vx5bnW6yohB6H9bLq8sJbaPLHtwZLtQPB32Pvy

Note: cargo check -p ergo-nipopow --no-default-features fails inside ergotree-ir on pristine develop as well (verified with these changes stashed) — pre-existing and unrelated.

…rusted u32 prefixes

Scorex Vec<T> parsing, NipopowProof/PoPowHeader parsing, and
ReducedTransaction parsing pre-allocated buffers from u32 count/length
prefixes taken from untrusted input, so a few bytes could abort the
process with an allocation failure instead of returning a parse error
(sibling of the ergotree-ir paths fixed for ergoplatform#944).

- sigma-ser: cap Vec<T> pre-allocation at 1024; add
  scorex_parse_bytes_exact, which grows the buffer only as bytes arrive
  and errors exactly like read_exact on a short stream.
- ergo-nipopow: cap prefix/suffix-tail pre-allocations; read the
  PoPowHeader header and interlinks-proof buffers via
  scorex_parse_bytes_exact.
- ergo-lib: read the reduced transaction's embedded tx buffer via
  scorex_parse_bytes_exact, mapping Io back to SigmaParsingError::Io so
  the error on a short stream is unchanged.

Fixes ergoplatform#946
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

1 participant