Skip to content

fix(network): Enforce 160-entry cap in read_headers#10528

Merged
conradoplg merged 2 commits into
mainfrom
fix/read-headers-protocol-cap
May 1, 2026
Merged

fix(network): Enforce 160-entry cap in read_headers#10528
conradoplg merged 2 commits into
mainfrom
fix/read-headers-protocol-cap

Conversation

@upbqdn

@upbqdn upbqdn commented May 1, 2026

Copy link
Copy Markdown
Member

Reproduce and propose fix.

@claude claude Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Claude Code Review

This repository is configured for manual code reviews. Comment @claude review to trigger a review and subscribe this PR to future pushes, or @claude review once for a one-time review.

Tip: disable this comment in your organization's Code Review settings.

mpguerra
mpguerra previously approved these changes May 1, 2026
@upbqdn upbqdn changed the title fix(network): Enforce 160-entry cap in read_headers - GHSA-438q-jx8f-cccv fix(network): Enforce 160-entry cap in read_headers May 1, 2026
@upbqdn
upbqdn force-pushed the fix/ghsa-rgwx-8r98-p34c branch from 3c5a047 to 5f5b8d0 Compare May 1, 2026 13:15
@upbqdn
upbqdn force-pushed the fix/read-headers-protocol-cap branch from af1ce94 to d2dcc2b Compare May 1, 2026 13:15
@upbqdn
upbqdn force-pushed the fix/ghsa-rgwx-8r98-p34c branch from 5f5b8d0 to 0ad6ff6 Compare May 1, 2026 15:25
@upbqdn
upbqdn force-pushed the fix/read-headers-protocol-cap branch from d2dcc2b to 5d0b956 Compare May 1, 2026 15:25
@conradoplg
conradoplg force-pushed the fix/ghsa-rgwx-8r98-p34c branch from 0ad6ff6 to 397ab72 Compare May 1, 2026 17:04
Base automatically changed from fix/ghsa-rgwx-8r98-p34c to main May 1, 2026 17:05
@conradoplg
conradoplg dismissed mpguerra’s stale review May 1, 2026 17:05

The base branch was changed.

…on bypass

Add a test proving that the codec accepts 161 headers in an inbound
`headers` message, exceeding the Zcash protocol cap of 160. The receive
path relies on TrustedPreallocate (ceiling ≈ 1 409) instead of
enforcing MAX_FIND_BLOCK_HEADERS_RESULTS.
…8q-jx8f-cccv)

read_headers() relied on TrustedPreallocate which allows up to 1,409
CountedHeader entries per message — ~8.8x the Zcash protocol limit of
160. Unauthenticated peers could force excessive preallocation and parse
work by sending oversized headers messages.

Deserialize the CompactSize count explicitly and reject messages
exceeding MAX_HEADERS_PER_MESSAGE (160) before parsing any entries.
The new constant lives in zebra-chain so both zebra-network (receive)
and zebra-state (send) can reference the same protocol value.
@conradoplg
conradoplg force-pushed the fix/read-headers-protocol-cap branch from 5d0b956 to 81f80c7 Compare May 1, 2026 17:06
@conradoplg
conradoplg merged commit 85303ea into main May 1, 2026
87 of 109 checks passed
@conradoplg
conradoplg deleted the fix/read-headers-protocol-cap branch May 1, 2026 17:07
oxarbitrage pushed a commit to dingledropper/zebra that referenced this pull request May 25, 2026
…ol-level constants

`block::Hash::max_allocation` previously returned 65,535 (derived from
`MAX_PROTOCOL_MESSAGE_LEN / 32`), and `CountedHeader::max_allocation`
returned ~1,409 (same message-size-derived shape). Both are reachable via
`Vec::zcash_deserialize` from any post-handshake peer through `getblocks`,
`getheaders`, and `headers` messages. A peer can claim up to those counts
and force `Vec::with_capacity` preallocation before any payload bytes are
read.

Cap at protocol-level constants:
- `MAX_BLOCK_LOCATOR_LENGTH = 101`, a new `block`-module constant that
  matches Bitcoin Core's `MAX_LOCATOR_SZ` in `net_processing.cpp` (which
  zcashd inherits).
- The existing `MAX_HEADERS_PER_MESSAGE = 160` constant from
  `crate::serialization::zcash_serialize`, already enforced on the sending
  side and at the codec level for `read_headers` (PR ZcashFoundation#10528).

Same fix shape applied to `AddrV1`/`AddrV2` in PR ZcashFoundation#10494 for
GHSA-xr93-pcq3-pxf8.

CWE-770 (Allocation of Resources Without Limits or Throttling).

Tests:
- `block_hash_max_allocation` proptest updated to assert
  `Hash::max_allocation() == MAX_BLOCK_LOCATOR_LENGTH`.
- `counted_header_max_allocation` proptest updated to assert
  `CountedHeader::max_allocation() == MAX_HEADERS_PER_MESSAGE as u64`.
- Test-only constants `BLOCK_HEADER_LENGTH` and `MIN_COUNTED_HEADER_LEN`,
  which were used by the old message-size-derived formula in `header.rs`,
  moved into the `preallocate` test module where they are the only
  consumers.
mergify Bot pushed a commit that referenced this pull request May 25, 2026
…ol-level constants (#10570)

`block::Hash::max_allocation` previously returned 65,535 (derived from
`MAX_PROTOCOL_MESSAGE_LEN / 32`), and `CountedHeader::max_allocation`
returned ~1,409 (same message-size-derived shape). Both are reachable via
`Vec::zcash_deserialize` from any post-handshake peer through `getblocks`,
`getheaders`, and `headers` messages. A peer can claim up to those counts
and force `Vec::with_capacity` preallocation before any payload bytes are
read.

Cap at protocol-level constants:
- `MAX_BLOCK_LOCATOR_LENGTH = 101`, a new `block`-module constant that
  matches Bitcoin Core's `MAX_LOCATOR_SZ` in `net_processing.cpp` (which
  zcashd inherits).
- The existing `MAX_HEADERS_PER_MESSAGE = 160` constant from
  `crate::serialization::zcash_serialize`, already enforced on the sending
  side and at the codec level for `read_headers` (PR #10528).

Same fix shape applied to `AddrV1`/`AddrV2` in PR #10494 for
GHSA-xr93-pcq3-pxf8.

CWE-770 (Allocation of Resources Without Limits or Throttling).

Tests:
- `block_hash_max_allocation` proptest updated to assert
  `Hash::max_allocation() == MAX_BLOCK_LOCATOR_LENGTH`.
- `counted_header_max_allocation` proptest updated to assert
  `CountedHeader::max_allocation() == MAX_HEADERS_PER_MESSAGE as u64`.
- Test-only constants `BLOCK_HEADER_LENGTH` and `MIN_COUNTED_HEADER_LEN`,
  which were used by the old message-size-derived formula in `header.rs`,
  moved into the `preallocate` test module where they are the only
  consumers.

Co-authored-by: dingledropper <dingledropper@users.noreply.github.com>
judah-caruso pushed a commit to ShieldedLabs/zebra-crosslink-staging that referenced this pull request May 28, 2026
…n#10528)

* test(network): reproduce GHSA-438q-jx8f-cccv read_headers preallocation bypass

Add a test proving that the codec accepts 161 headers in an inbound
`headers` message, exceeding the Zcash protocol cap of 160. The receive
path relies on TrustedPreallocate (ceiling ≈ 1 409) instead of
enforcing MAX_FIND_BLOCK_HEADERS_RESULTS.

* fix(network): enforce 160-entry protocol cap in read_headers (GHSA-438q-jx8f-cccv)

read_headers() relied on TrustedPreallocate which allows up to 1,409
CountedHeader entries per message — ~8.8x the Zcash protocol limit of
160. Unauthenticated peers could force excessive preallocation and parse
work by sending oversized headers messages.

Deserialize the CompactSize count explicitly and reject messages
exceeding MAX_HEADERS_PER_MESSAGE (160) before parsing any entries.
The new constant lives in zebra-chain so both zebra-network (receive)
and zebra-state (send) can reference the same protocol value.

---------

Co-authored-by: Alfredo Garcia <oxarbitrage@gmail.com>
judah-caruso pushed a commit to ShieldedLabs/zebra-crosslink-staging that referenced this pull request May 28, 2026
…ol-level constants (ZcashFoundation#10570)

`block::Hash::max_allocation` previously returned 65,535 (derived from
`MAX_PROTOCOL_MESSAGE_LEN / 32`), and `CountedHeader::max_allocation`
returned ~1,409 (same message-size-derived shape). Both are reachable via
`Vec::zcash_deserialize` from any post-handshake peer through `getblocks`,
`getheaders`, and `headers` messages. A peer can claim up to those counts
and force `Vec::with_capacity` preallocation before any payload bytes are
read.

Cap at protocol-level constants:
- `MAX_BLOCK_LOCATOR_LENGTH = 101`, a new `block`-module constant that
  matches Bitcoin Core's `MAX_LOCATOR_SZ` in `net_processing.cpp` (which
  zcashd inherits).
- The existing `MAX_HEADERS_PER_MESSAGE = 160` constant from
  `crate::serialization::zcash_serialize`, already enforced on the sending
  side and at the codec level for `read_headers` (PR ZcashFoundation#10528).

Same fix shape applied to `AddrV1`/`AddrV2` in PR ZcashFoundation#10494 for
GHSA-xr93-pcq3-pxf8.

CWE-770 (Allocation of Resources Without Limits or Throttling).

Tests:
- `block_hash_max_allocation` proptest updated to assert
  `Hash::max_allocation() == MAX_BLOCK_LOCATOR_LENGTH`.
- `counted_header_max_allocation` proptest updated to assert
  `CountedHeader::max_allocation() == MAX_HEADERS_PER_MESSAGE as u64`.
- Test-only constants `BLOCK_HEADER_LENGTH` and `MIN_COUNTED_HEADER_LEN`,
  which were used by the old message-size-derived formula in `header.rs`,
  moved into the `preallocate` test module where they are the only
  consumers.

Co-authored-by: dingledropper <dingledropper@users.noreply.github.com>
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.

4 participants