REDHAT-BUG-2536976: Command Injection
Unbounded multi-line response accumulation in SmtpResponseDecoder leads to memory-exhaustion DoS
A public GitHub Security Advisory (GHSA-pq4x-537v-r54q) describes the following issue:
Summary io.netty.handler.codec.smtp.SmtpResponseDecoder accumulates the multi-line details of an SMTP response into a per-connection ArrayList<CharSequence> that has NO count or cumulative-size cap. A malicious or MITM SMTP server that withholds the space-separated terminator line and streams unbounded 250-x\r\n continuation lines drives that list to grow without bound across decode() invocations, exhausting the JVM heap and killing the Netty-based SMTP client/MTA process (Denial of Service).
Root Cause this.details (SmtpResponseDecoder.java:35, ArrayList<CharSequence>) is written only at :66 (set to null on the terminator), :85 (new list), and :87 (add). A grep of the file for maxNum/size()/any cumulative cap returns only maxLineLength — a per-line bound, not an aggregate one. Accumulation resets ONLY on the space-separator branch (:64, resets this.details=null at :66); the '-' continuation branch (:79-89) never resets or caps. A server that sends only 250-x\r\n (continuation) and never 250 x\r\n (terminator) grows details forever. The decoded per-line frame is released at :94, but the retained String copies (:59) in details are not, so nothing frees the accumulated memory. LineBasedFrameDecoder.maxLength (LineBasedFrameDecoder.java:44-45,:112) bounds only each individual line — no aggregate/message-level cap exists anywhere in the module.
This is the unpatched structural sibling of CVE-2026-44891, which added a maxNumHeaders cap to StompSubframeDecoder (the only other LineBasedFrameDecoder subclass). SmtpResponseDecoder was never given the analogous cap. It is also the same bug class Netty already published at MEDIUM in GHSA-q4f6-jm68-57ww (unbounded per-connection queue growth -> DoS).
Impact Heap exhaustion -> OutOfMemoryError -> total availability loss (process crash) of the SMTP client/MTA. Client-side decoder: the attacker must occupy the server side of the connection (attacker-controlled MX for direct-to-MX delivery, user/tenant-supplied SMTP host, mail-testing tooling, or a STARTTLS-downgrade MITM on cleartext SMTP).
Proof of Concept Stand up an SMTP listener that, upon any client connection, repeatedly writes 250-x\r\n and never sends a space-separated terminator (250 x\r\n). Point a Netty pipeline containing SmtpResponseDecoder at it. details grows one retained String (~40-60 bytes) per 7-byte line until the client heap is exhausted.
Attack Chain 1. Entry: a Netty-based SMTP client/MTA (pipeline contains SmtpResponseDecoder) opens a connection to an attacker-controlled or MITM'd SMTP server. - Guard: none at connect; TLS is no barrier when the attacker owns the endpoint or downgrades STARTTLS on cleartext SMTP. - Bypass proof: the class is client-side by construction (decodes server responses; SmtpResponseDecoderTest.java:148 instantiates it for inbound decoding). MTAs connect to remote-controlled MX servers by design. 2. Check/Feed continuation lines: server streams 250-x\r\n (7 bytes, non-empty detail) repeatedly, each below maxLineLength. - Guard: LineBasedFrameDecoder.maxLength per-line bound (LineBasedFrameDecoder.java:112). - Bypass proof: each 7-byte line is far below maxLineLength; the check never inspects details. Every line takes the '-' branch (:79) -> details.add(detail) (:87). 3. Withhold terminator: server never sends the space-separated 250 x\r\n line. - Guard: the only reset of this.details is the space branch at :66; there is no count/size cap on the '-' path. - Bypass proof: writes to this.details occur only at :66 (null), :85 (new), :87 (add) - confirmed by grep; no details.size()/maxNum check exists. details is an instance field (:35) persisting across decode() calls. 4. Sink: this.details and its retained String elements grow without bound across decode() invocations (:87). - Guard: none - no aggregate frame/message limit; the per-line frame is released at :94 but the retained String copy (:59) is not. - Bypass proof: LineBasedFrameDecoder bounds only per-line length; nothing caps cumulative retained objects. 5. Impact: OutOfMemoryError -> DoS of the SMTP client/MTA process.
Bypass Evidence - On latest release tag netty-4.2.16.Final, git show netty-4.2.16.Final:.../SmtpResponseDecoder.java shows new ArrayList<CharSequence>(4) (:85) and details.add(detail) (:69/:87) with NO cap; only maxLineLength is present. - grep -rnE "maxNum|maxDetails|maxLines|TooLongFrameException" codec-smtp/ returns NONE — no aggregate cap anywhere in the module. - git log netty-4.2.16.Final..HEAD -- SmtpResponseDecoder.java is empty — no post-release fix. - Contrast: the sibling StompSubframeDecoder HAS maxNumHeaders -> TooLongFrameException; SMTP has no equivalent. - Dedup clean: netty's published advisories contain no SmtpResponseDecoder/codec-smtp memory-exhaustion entry (the only codec-smtp advisory, GHSA-jq43-27x9-3v86, is SMTP command injection — a different class).
Affected Versions io.netty:netty-codec-smtp <= 4.2.16.Final (all released versions; the 4.1.x line is likewise affected — the decoder has had no aggregate cap since its 2016 introduction).
Suggested Fix Add a maxNumLines (and/or cumulative-detail-size) parameter to SmtpResponseDecoder, incrementing per continuation line and throwing TooLongFrameException when exceeded — mirroring StompSubframeDecoder's maxNumHeaders. Provide a safe default (e.g., 128) via an overloaded constructor.
--- Reported by zx (Jace) — GitHub: @manus-use
Affected: - maven:io.netty:netty-codec-smtp affected >= 4.2.0.Final, <= 4.2.17.Final; fixed unknown - maven:io.netty:netty-codec-smtp affected >= 4.1.0.Final, <= 4.1.137.Final; fixed unknown
Fixed versions: see advisory
Advisory: https://github.com/netty/netty/security/advisories/GHSA-pq4x-537v-r54q
Affected Software
Event History
Frequently Asked Questions
Which systems are exposed to this denial-of-service condition?
Netty-based SMTP clients or MTAs that use io.netty.handler.codec.smtp.SmtpResponseDecoder are exposed when communicating with a malicious SMTP server or when an attacker can act as a man-in-the-middle for the SMTP connection.
What does an attacker need to do to trigger the issue?
The attacker must send SMTP continuation responses such as repeated "250-x\r\n" lines while withholding the space-separated terminating response line. This causes response details to remain accumulated across decoder invocations.
Does the decoder's maximum line-length setting prevent memory exhaustion?
No. The described maxLineLength limit applies to individual lines only; it does not cap the number of accumulated continuation lines or their total size.