Skip to content

fix(binarycodec): guard BinaryParser::read bounds + limit STObject decode depth - #344

Open
e-desouza wants to merge 1 commit into
mainfrom
fix/binary-wrappers-panics
Open

fix(binarycodec): guard BinaryParser::read bounds + limit STObject decode depth#344
e-desouza wants to merge 1 commit into
mainfrom
fix/binary-wrappers-panics

Conversation

@e-desouza

Copy link
Copy Markdown
Collaborator

Why

BinaryParser::read(n) sliced self.0[..n] before calling skip_bytes(n), which is where the bounds check lived. On a truncated binary XRPL blob, read panicked instead of returning an error — reachable from any network-sourced binary data via decode().

decode_st_object and decode_field_value form a mutually recursive pair with no depth limit. A crafted binary blob embedding deeply nested STObject headers (~4–16 KB) overflows the stack. Stack overflows abort the process unconditionally — they are not catchable with catch_unwind.

What changed

  • BinaryParser::read: added if n > self.0.len() guard returning UnexpectedParserSkipOverflow { max, found } before the slice.
  • Added const MAX_DECODE_DEPTH: u32 = 32.
  • Added MaxDecodeDepthExceeded { max: u32 } variant to XRPLBinaryCodecException.
  • decode_st_object, decode_field_value, decode_st_array now accept a depth: u32 parameter; the public decode() entry passes depth: 0.
  • decode_st_object returns Err(MaxDecodeDepthExceeded) when depth > MAX_DECODE_DEPTH.

How to validate

cargo test --release

New tests: test_binary_parser_read_truncated_returns_error, test_decode_st_object_depth_limit.

@codecov

codecov Bot commented Jul 12, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 94.64286% with 3 lines in your changes missing coverage. Please review.
✅ Project coverage is 86.72%. Comparing base (c83df4f) to head (778d2ed).

Files with missing lines Patch % Lines
src/core/binarycodec/binary_wrappers.rs 94.54% 3 Missing ⚠️
Additional details and impacted files

Impacted file tree graph

@@           Coverage Diff           @@
##             main     #344   +/-   ##
=======================================
  Coverage   86.71%   86.72%           
=======================================
  Files         252      252           
  Lines       32630    32676   +46     
=======================================
+ Hits        28296    28339   +43     
- Misses       4334     4337    +3     
Flag Coverage Δ
integration 71.32% <ø> (ø)
unit 87.34% <94.64%> (+<0.01%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

Files with missing lines Coverage Δ
src/core/binarycodec/exceptions.rs 0.00% <ø> (ø)
src/core/binarycodec/mod.rs 90.55% <100.00%> (ø)
src/core/binarycodec/binary_wrappers.rs 89.94% <94.54%> (+0.30%) ⬆️
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Copilot AI 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.

Pull request overview

This PR hardens the XRPL binary decoder against malformed or adversarial inputs by preventing panics in BinaryParser::read and bounding recursive decoding of nested STObject/STArray structures.

Changes:

  • Added an explicit bounds check in BinaryParser::read(n) to return an error instead of panicking on out-of-bounds slices.
  • Introduced a maximum decode depth (MAX_DECODE_DEPTH) and a new MaxDecodeDepthExceeded error to prevent stack overflows from deeply nested payloads.
  • Updated decoding helpers to thread a depth counter through recursive calls, and added regression tests for both failure modes.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated no comments.

File Description
src/core/binarycodec/mod.rs Updates the decode() entrypoint to initialize recursive decoding with depth = 0.
src/core/binarycodec/exceptions.rs Adds MaxDecodeDepthExceeded to the binary codec exception set for depth-limit enforcement.
src/core/binarycodec/binary_wrappers.rs Implements read() bounds guard, adds decode depth limit enforcement, threads depth through decode functions, and adds regression tests.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

…rsion depth limit

OOBIDX-001: BinaryParser::read() sliced self.0[..n] before checking bounds,
causing a panic on truncated input. Now checks n > self.0.len() first and
returns UnexpectedParserSkipOverflow, consistent with skip_bytes().

RECURSEDES-001: decode_st_object / decode_field_value / decode_st_array were
mutually recursive with no depth limit. A crafted payload with > 32 levels of
nested STObject/STArray fields would exhaust the stack (uncatchable SIGSEGV).
Adds MAX_DECODE_DEPTH = 32 and a new MaxDecodeDepthExceeded error variant;
depth is threaded through all three functions and checked at entry to
decode_st_object. The public decode() entry point passes depth 0.

Two regression tests added: one asserts the exact UnexpectedParserSkipOverflow
error on read() with 10 bytes requested from a 2-byte buffer; the other
constructs 34 nested FinalFields (0xE7) headers and asserts MaxDecodeDepthExceeded
is returned rather than a stack overflow.
@e-desouza
e-desouza force-pushed the fix/binary-wrappers-panics branch from 778d2ed to f56a3ed Compare July 15, 2026 16:28
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.

2 participants