Skip to content

fix: bound Jackson payload decompression size, unlimited by default (#3491, #3515) - #3523

Open
pjfanning wants to merge 2 commits into
apache:1.7.xfrom
pjfanning:jackson-unlimited-decompression-17
Open

fix: bound Jackson payload decompression size, unlimited by default (#3491, #3515)#3523
pjfanning wants to merge 2 commits into
apache:1.7.xfrom
pjfanning:jackson-unlimited-decompression-17

Conversation

@pjfanning

Copy link
Copy Markdown
Member

Motivation

Backport of #3491 and its follow-up #3515 to 1.7.x:

  • fix: bound Jackson payload decompression size #3491 bounded Jackson payload decompression, which was previously unbounded (an
    unbounded gzip copy, and an LZ4 decompressor allocation sized from a wire-declared
    length with no upper check) — a small, well-formed message could drive an
    OutOfMemoryError on deserialization.
  • fix: default the Jackson max-decompressed-size to unlimited #3515 changed the bound's default from 256 MiB to unlimited (-1/unlimited both
    accepted), so a patch release does not start rejecting a payload an existing system
    legitimately exchanges. The bound stays available and opt-in.

Modification

Cherry-pick of fd19b01 (#3491) and 9d80429 (#3515, itself a squash of the
default-to-unlimited change and the later "also accept unlimited" follow-up from
review).

Scope note: both commits also touch serialization-jackson3, which does not exist
as a module on 1.7.x (Jackson 3 support is new in the 2.x line — see the
"New configuration" section of the 2.x migration guide, PR2348). Those hunks were
dropped; only the serialization-jackson (Jackson 2) changes are included here.

2.12 adaptation: the merged maxDecompressedSize used raw.toLongOption, which
Scala 2.12 does not have. Rewritten with toLong in a try/catch NumberFormatException,
same shape as the fix already needed for the #3503 backport (#3517).

Result

  • pekko.serialization.jackson.compression.max-decompressed-size defaults to
    unlimited; both unlimited and a negative number such as -1 disable the bound.
  • A configured size (e.g. 256 MiB) bounds decompression exactly as in fix: bound Jackson payload decompression size #3491: an
    over-expanding gzip payload or an LZ4 payload declaring a length past the limit is
    rejected with an IllegalArgumentException before the large allocation, instead of
    risking OutOfMemoryError.

Tests

  • sbt "++ 2.12.21 serialization-jackson/Test/compile" — clean, validating Scala 2.12
  • sbt "serialization-jackson/testOnly org.apache.pekko.serialization.jackson.*" — 123 passed, 1 pending
  • sbt "serialization-jackson/scalafmtCheckAll" — clean

References

Backport of #3491 and #3515.

Motivation:
JacksonSerializer.decompress inflated gzip payloads with an unbounded
transferTo, and passed the lz4 decompressed length declared on the wire
straight to the decompressor as the allocation size. A small, well-formed
message could therefore declare (or expand to) an arbitrarily large size and
drive an OutOfMemoryError on deserialization.

Modification:
Add a `compression.max-decompressed-size` setting (default 256 MiB) to the
jackson and jackson3 modules. On deserialization the gzip path copies through
a bounded loop and the lz4 path rejects a declared length that is negative or
over the cap, before allocating. Applies regardless of the `algorithm`
setting, since decompression is chosen by the payload's magic bytes.

Result:
A payload that would decompress beyond the cap is rejected with an
IllegalArgumentException instead of exhausting the heap.

Tests:
- sbt "serialization-jackson/testOnly *JacksonJsonSerializerSpec" "serialization-jackson3/testOnly *JacksonJsonSerializerSpec" - 71 passed each, incl. new gzip/lz4 cap tests
- sbt "serialization-jackson/mimaReportBinaryIssues" - no issues (changed symbols are @InternalApi/private)
- sbt scalafmt for changed main and test sources

References:
None - found while reviewing the draft threat model in apache#3478
)

* fix: default the Jackson max-decompressed-size to unlimited

Motivation:
A bounded default could reject a payload an existing system legitimately
exchanges, so a patch release carrying the 256 MiB default from apache#3491
could break running systems on upgrade. The bound should be opt-in,
matching the change made to pekko.serialization.max-decompressed-size
in apache#3502.

Modification:
Default pekko.serialization.jackson.compression.max-decompressed-size
(and the jackson3 equivalent) to -1, meaning no limit and matching the
behaviour of releases before apache#3491. A negative maximum skips the gzip
size check and the LZ4 declared-size check; a negative declared LZ4
size is still rejected, since it is malformed regardless of the limit.
Config's getBytes refuses negative numbers, so the setting is read as a
plain long first and as a memory size only when that is not a negative
number.

Result:
Jackson payload decompression is unbounded by default; configuring a
size such as 256 MiB bounds it.

Tests:
- sbt "serialization-jackson/testOnly org.apache.pekko.serialization.jackson.*" - 122 passed
- sbt "serialization-jackson3/testOnly org.apache.pekko.serialization.jackson3.*" - 120 passed
- sbt "serialization-jackson/scalafmtCheckAll" "serialization-jackson3/scalafmtCheckAll" - clean
- sbt "serialization-jackson/mimaReportBinaryIssues" - no issues

References:
Refs apache#3491, Refs apache#3502

* also accept "unlimited" for max-decompressed-size

Motivation:
Review on apache#3515 noted that Pekko is inconsistent about unlimited
spellings and an explicit keyword is clearer than a magic number, while
the neighbouring read.max-document-length and read.max-token-count
settings use -1. Accept both.

Modification:
The setting reads "unlimited" or any negative number as no limit; the
reference.conf default is written as `unlimited`. Applied to both
serialization-jackson and serialization-jackson3, with a test each for
the keyword.

Result:
`max-decompressed-size = unlimited` and `= -1` both disable the bound.

Tests:
- sbt "serialization-jackson/testOnly org.apache.pekko.serialization.jackson.*" - 123 passed
- sbt "serialization-jackson3/testOnly org.apache.pekko.serialization.jackson3.*" - 121 passed
- sbt "serialization-jackson/scalafmtCheckAll" "serialization-jackson3/scalafmtCheckAll" - clean

References:
Refs apache#3515
@pjfanning pjfanning added this to the 1.7.1 milestone Sep 3, 2026
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.

1 participant