GHSA-pgrf-4654-3gq8: Integer Overflow
Summary
io.netty.incubator:netty-incubator-codec-bhttp uses attacker-controlled Binary HTTP variable-length integers as long values but accumulates them into int offsets. Large valid varint lengths wrap the internal offset negative, leading to unchecked ArrayIndexOutOfBoundsException / IndexOutOfBoundsException from a tiny malformed BHTTP payload. A remote peer can trigger connection-level denial of service in applications that expose BinaryHttpParser / BinaryHttpDecoder to untrusted input.
Details
In codec-bhttp/src/main/java/io/netty/incubator/codec/bhttp/BinaryHttpParser.java, several parser paths store cumulative byte offsets in int sumBytes and then add attacker-controlled long lengths using compound assignment. In Java, int += long narrows the result back to int, so a length such as 2^31 wraps sumBytes negative.
Primary request-control-data path:
- readRequestHead(...) declares int sumBytes = 0 at BinaryHttpParser.java:386. - It reads methodLength as a long at BinaryHttpParser.java:394. - It performs sumBytes += methodLength at BinaryHttpParser.java:395, narrowing the result to int. - If methodLength is 2^31, sumBytes wraps negative and bypasses if (sumBytes >= in.readableBytes()) return null at BinaryHttpParser.java:396-398. - The parser then computes schemeLengthIdx = in.readerIndex() + sumBytes and calls in.getByte(schemeLengthIdx) at BinaryHttpParser.java:401-402, producing a negative index exception.
The same pattern is present in header parsing:
- readFieldLine(...) uses int sumBytes and adds long nameLength / long valueLength at BinaryHttpParser.java:659-680. - valueLengthIdx = nameIdx + (int) nameLength at BinaryHttpParser.java:674 can also overflow.
getIndeterminateLength(...) similarly uses int sumBytes and long possibleTerminator at BinaryHttpParser.java:544-553.
Proof of concept
Safe local verification performed in this repository. After compiling codec-bhttp, the following minimal verifier uses a 15-byte payload:
java import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import io.netty.incubator.codec.bhttp.BinaryHttpParser;
public final class VerifyBhttpOverflow { public static void main(String[] args) { byte[] payload = new byte[] { 0x00, (byte)0xc0, 0x00, 0x00, 0x00, (byte)0x80, 0x00, 0x00, 0x00, 0x47, 0x45, 0x54, 0x58, 0x58, 0x58 }; ByteBuf input = Unpooled.wrappedBuffer(payload); try { new BinaryHttpParser(8192).parse(input, false); System.out.println("returned"); } catch (Throwable t) { System.out.println(t.getClass().getName()); System.out.println(t.getMessage()); } } }
Payload interpretation:
- 00: known-length request frame indicator. - c000000080000000: valid 8-byte varint encoding of 0x80000000 (2^31) as the method length. - 474554585858: a few dummy bytes so the parser proceeds far enough to compute the next index.
Observed result:
text java.lang.ArrayIndexOutOfBoundsException Index -2147483639 out of bounds for length 15
The parser should reject the malformed/incomplete message with a controlled decoder exception or return null awaiting more bytes; it should not allow integer wraparound to reach unchecked buffer indexing.
Impact
A remote peer can trigger an unchecked exception in the Binary HTTP decoder using a tiny payload. In typical Netty pipelines this closes or fails the affected channel. Depending on application-level exception handling, repeated payloads can cause sustained denial of service for exposed BHTTP endpoints. No memory corruption or information disclosure was observed because the failure occurs in Java/Netty bounds checks.
Suggested remediation
- Use long for all cumulative byte counts derived from protocol lengths. - Before converting any protocol length to int, verify it is non-negative, no larger than Integer.MAXVALUE, and no larger than available readable bytes and configured limits. - Replace sumBytes >= in.readableBytes() checks with precise checked arithmetic that permits exact-boundary complete fields but rejects impossible lengths. - Throw a controlled CorruptedFrameException / TooLongFrameException for invalid or unsupported lengths. - Add regression tests for 8-byte varint lengths at and above Integer.MAXVALUE in request control data, response control data, known and indeterminate field sections, and field lines.
References
- codec-bhttp/src/main/java/io/netty/incubator/codec/bhttp/BinaryHttpParser.java:386-402 - codec-bhttp/src/main/java/io/netty/incubator/codec/bhttp/BinaryHttpParser.java:659-680 - codec-bhttp/src/main/java/io/netty/incubator/codec/bhttp/BinaryHttpParser.java:544-553 - RFC 9292: Binary Representation of HTTP Messages - RFC 9000 variable-length integer encoding
Affected Software
Remediation
Recommended actions to resolve this vulnerability, in priority order.
- Upgrade
Upgrade
maven/io.netty.incubator:netty-incubator-codec-bhttpto a version that resolves this vulnerability.Fixed in 0.0.23.Final - Upgrade
Upgrade
io.netty.incubator:netty-incubator-codec-bhttpto a version that resolves this vulnerability.Patch codec-bhttp/src/main/java/io/netty/incubator/codec/bhttp/BinaryHttpParser.java:386-402 - Configuration
Update BinaryHttpParser so all cumulative byte counts derived from protocol variable-length integers use long (not int), and before converting any protocol length to int verify: (1) non-negative, (2) no larger than Integer.MAX_VALUE, and (3) no larger than available readable bytes and configured limits. Replace any int sumBytes usage and compound assignments like 'sumBytes += methodLength' (where methodLength can be 2^31) with precise checked arithmetic to prevent wraparound into negative offsets.
io.netty.incubator.codec.bhttp.BinaryHttpParser Cumulative byte-count arithmetic for protocol lengths (sumBytes) = use long for cumulative byte counts; reject negative/overflow/insufficient-length inputs before converting to int - Configuration
Modify BinaryHttpParser bounds logic so that when computed indices such as schemeLengthIdx = in.readerIndex() + sumBytes would be invalid (e.g., due to wraparound that would later cause in.getByte(schemeLengthIdx) to throw), the parser rejects the message with a controlled CorruptedFrameException / TooLongFrameException (and/or returns null only for incomplete/await-more-bytes cases).
io.netty.incubator.codec.bhttp.BinaryHttpParser (index computation) Controlled exception for invalid/unsupported lengths = throw controlled CorruptedFrameException / TooLongFrameException on invalid lengths instead of unchecked index exceptions - Configuration
Update readFieldLine (...) so it does not use int sumBytes for cumulative offsets; since it adds long nameLength/valueLength, keep offsets as long and perform overflow/available-bytes checks. Ensure computations like valueLengthIdx = nameIdx + (int)nameLength cannot overflow; reject with a controlled decoder exception or return null awaiting more bytes.
io.netty.incubator.codec.bhttp.BinaryHttpParser (field parsing) readFieldLine cumulative offsets = use long for sumBytes/name/value offsets; prevent overflow when computing valueLengthIdx - Configuration
Update readRequestHead (...) where int sumBytes = 0 is declared to use long cumulative byte counts with checked arithmetic, applying the same non-negative/Integer.MAX_VALUE/available-readable-bytes validation before using offsets.
io.netty.incubator.codec.bhttp.BinaryHttpParser (request head) readRequestHead cumulative offsets = use long instead of int for sumBytes - Configuration
Update getIndeterminateLength (...) to avoid int sumBytes and checked arithmetic issues when adding attacker-influenced lengths; ensure invalid or impossible lengths are rejected via controlled CorruptedFrameException / TooLongFrameException rather than allowing unchecked out-of-bounds.
io.netty.incubator.codec.bhttp.BinaryHttpParser (indeterminate length) getIndeterminateLength cumulative offsets = use long instead of int for sumBytes and checked arithmetic - Compensating control
Mitigate connection-level DoS by ensuring BHTTP endpoints that expose BinaryHttpParser/BinaryHttpDecoder to untrusted input are fronted by external rate limiting / traffic shaping (e.g., at a load balancer/WAF/reverse proxy) so repeated tiny malformed payloads cannot sustain denial of service.
Event History
Frequently Asked Questions
Which deployments are exposed to this issue?
Applications that expose BinaryHttpParser or BinaryHttpDecoder to untrusted Binary HTTP input are exposed. A remote peer can supply the malformed payload.
What does an attacker need to exploit it?
The attacker needs only network access to send a crafted BHTTP payload containing a large but valid variable-length integer length. No privileges or user interaction are required.
What is the practical impact of a successful exploit?
A tiny malformed payload can cause an unchecked ArrayIndexOutOfBoundsException or IndexOutOfBoundsException, resulting in connection-level denial of service. The supplied severity vector indicates no confidentiality or integrity impact.
How can I look for exploitation or exposure in logs?
Look for ArrayIndexOutOfBoundsException or IndexOutOfBoundsException occurring while processing Binary HTTP input, especially in BinaryHttpParser parsing paths. The triggering input may be very small despite containing an oversized valid varint length.