Skip to content

Fix XDR stream DoS via memory exhaustion and decouple hash validation from Close - #5900

Merged
tamirms merged 5 commits into
stellar:mainfrom
tamirms:xdrestream
Feb 17, 2026
Merged

Fix XDR stream DoS via memory exhaustion and decouple hash validation from Close#5900
tamirms merged 5 commits into
stellar:mainfrom
tamirms:xdrestream

Conversation

@tamirms

@tamirms tamirms commented Feb 13, 2026

Copy link
Copy Markdown
Contributor

Add a record size limit (default 64MB) to ReadOne to prevent an attacker from triggering massive allocations with a small 4-byte header claiming up to 2GB. Replace SetExpectedHash/Close hash validation with an explicit ValidateHash method so Close only releases resources and early close never triggers unnecessary stream draining.

PR Checklist

PR Structure

  • This PR has reasonably narrow scope (if not, break it down into smaller PRs).
  • This PR avoids mixing refactoring changes with feature changes (split into two PRs
    otherwise).
  • This PR's title starts with name of package that is most changed in the PR, ex.
    services/friendbot, or all or doc if the changes are broad or impact many
    packages.

Thoroughness

  • This PR adds tests for the most critical parts of the new functionality or fixes.
  • I've updated any docs (developer docs, .md
    files, etc... affected by this change). Take a look in the docs folder for a given service,
    like this one.

Release planning

  • I've reviewed the changes in this PR and if I consider them worthwhile for being mentioned on release notes then I have updated the relevant CHANGELOG.md within the component folder structure. For example, if I changed horizon, then I updated (services/horizon/CHANGELOG.md. I add a new line item describing the change and reference to this PR. If I don't update a CHANGELOG, I acknowledge this PR's change may not be mentioned in future release notes.
  • I've decided if this PR requires a new major/minor version according to
    semver, or if it's mainly a patch change. The PR is targeted at the next
    release branch if it's not a patch change.

What

  • Fix DoS via memory exhaustion: ReadOne now validates the 4-byte length prefix against a configurable max (default 64MB) before calling Grow, preventing an attacker from triggering up to 2GB
    allocations with just 4 bytes of input
  • Decouple hash validation from Close: Replace SetExpectedHash/Close pattern with explicit ValidateHash(hash) method so Close only releases resources — early close via defer no longer drains the entire
    decompressed stream
  • Callers updated: streamBucket now calls ValidateHash after the read loop completes, before closing the stream

Copilot AI review requested due to automatic review settings February 13, 2026 20:44
… from Close

Add a record size limit (default 64MB) to ReadOne to prevent an attacker
from triggering massive allocations with a small 4-byte header claiming
up to 2GB. Replace SetExpectedHash/Close hash validation with an explicit
ValidateHash method so Close only releases resources and early close
never triggers unnecessary stream draining.

Co-Authored-By: Claude Opus 4.6 <[email protected]>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

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 addresses a critical DoS vulnerability in XDR stream handling and improves the API design for hash validation. The vulnerability allowed an attacker to trigger up to 2GB memory allocations with just 4 bytes of malicious input. The fix introduces a configurable record size limit (default 64MB) that is checked before buffer allocation. Additionally, the PR decouples hash validation from the Close() method by introducing an explicit ValidateHash() method, ensuring that early close operations (via defer) don't unnecessarily drain entire decompressed streams.

Changes:

  • Adds DoS protection with a default 64MB record size limit checked before buffer allocation in ReadOne
  • Replaces SetExpectedHash/Close pattern with explicit ValidateHash method for clearer separation of concerns
  • Updates all callers to use the new ValidateHash pattern

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 6 comments.

File Description
xdr/xdrstream.go Adds record size validation, new ValidateHash method, removes hash validation from Close, adds SetMaxRecordSize configuration
xdr/xdrstream_test.go Updates tests to use ValidateHash, adds comprehensive tests for record size limits and boundaries
ingest/checkpoint_change_reader.go Simplifies newXDRStream by removing SetExpectedHash, adds explicit ValidateHash call in streamBucket after successful iteration
ingest/checkpoint_change_reader_test.go Removes tests for deprecated ExpectedHash method

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

Comment thread xdr/xdrstream.go
Comment thread xdr/xdrstream.go
Comment thread xdr/xdrstream_test.go
Comment thread xdr/xdrstream.go Outdated
Comment thread xdr/xdrstream.go
Comment thread xdr/xdrstream.go
tamirms and others added 3 commits February 13, 2026 20:51
Verify that ValidateHash returns an error when the stream reader has
already been closed, using a custom closedReader that mimics real
file/network close behavior.

Co-Authored-By: Claude Opus 4.6 <[email protected]>
@tamirms
tamirms requested a review from a team February 13, 2026 21:04
@tamirms
tamirms merged commit f690fb0 into stellar:main Feb 17, 2026
11 checks passed
@tamirms
tamirms deleted the xdrestream branch February 17, 2026 08:52
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.

3 participants