Skip to content

fix(logstash): [OBE-10712] bound zlib frame inflation, reject nested compressed frames - #142

Merged
ajayshekar-s1 merged 2 commits into
Sentinel-One:masterfrom
JuanMantica45:oom-logstash-decompression
Aug 12, 2026
Merged

fix(logstash): [OBE-10712] bound zlib frame inflation, reject nested compressed frames#142
ajayshekar-s1 merged 2 commits into
Sentinel-One:masterfrom
JuanMantica45:oom-logstash-decompression

Conversation

@JuanMantica45

Copy link
Copy Markdown
Contributor

What

  • New max_decompressed_bytes (default 256 MiB) caps how far a Beats C frame may inflate. Previously read_to_end with no bound, so a zip bomb from any peer could exhaust the heap.
  • Nested C frames are rejected outright; previously they recursed with no depth limit.

Why 256 MiB

One C frame carries an entire window, not a single event — Filebeat's bulk_max_size defaults to 2048 and go-lumber's maxWindowSize allows 10000 — so legitimate inflated batches reach tens of MiB and the bound must sit well above that.

It applies per frame, so peak memory is this value × the concurrent connection count. Set a finite connection_limit if that product matters for your deployment.

The reader is capped at max + 1 rather than max so "exactly at the limit" stays distinguishable from "truncated at the limit" — capping at max alone rejected a payload of exactly max_decompressed_bytes.

Testing

The two decompression-bomb tests were behind the logstash-integration-tests feature and never ran in normal CI. They are now unit tests, plus added boundary, buffer-drain and stream-continuation coverage. 17 passing.

Jira: OBE-10712

…d frames
The Beats `C` frame was inflated with read_to_end and no upper bound, so a
zip bomb from any peer could exhaust the heap. A `C` frame nested inside a `C`
frame also recursed with no depth limit.
max_decompressed_bytes defaults to 256 MiB. One `C` frame carries an entire
window rather than a single event -- Filebeat's bulk_max_size defaults to 2048
and go-lumber's maxWindowSize allows 10000 -- so legitimate inflated batches
reach tens of MiB and the bound must sit well above that. It applies per frame,
so peak memory is this value times the concurrent connection count; set a finite
connection_limit if that product matters.
The reader is capped at max + 1 rather than max so that "exactly at the limit"
stays distinguishable from "truncated at the limit"; capping at max alone
rejected a payload of exactly max_decompressed_bytes.
Nested frames are rejected with can_continue() == false, since a peer sending
them is not recoverable.
The two decompression-bomb tests were behind the logstash-integration-tests
feature and never ran in normal CI; they are now unit tests, with added
boundary, buffer-drain and stream-continuation coverage.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@ajayshekar-s1
ajayshekar-s1 merged commit 3a4b19d into Sentinel-One:masterAug 12, 2026
Sign up for freeto 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

@JuanMantica45@ajayshekar-s1