diff --git a/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/hpack/HPackDecoder.java b/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/hpack/HPackDecoder.java index 3ee592649..627de5631 100644 --- a/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/hpack/HPackDecoder.java +++ b/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/hpack/HPackDecoder.java @@ -104,24 +104,28 @@ static int peekByte(final ByteBuffer src) throws HPackException { static int decodeInt(final ByteBuffer src, final int n) throws HPackException { final int nbits = 0xff >>> (8 - n); - int value = readByte(src) & nbits; + long value = readByte(src) & nbits; if (value < nbits) { - return value; + return (int) value; } int m = 0; while (m < 32) { final int b = readByte(src); - if ((b & 0x80) != 0) { - value += (b & 0x7f) << m; - m += 7; - } else { - if (m == 28 && (b & 0xf8) != 0) { - break; - } - value += b << m; - return value; + + value += (long) (b & 0x7f) << m; + + // HPACK integers are unsigned; reject values that exceed Integer.MAX_VALUE to avoid signed overflow + // and ensure malformed input is reported as HPackException (protocol error) rather than runtime failure. + if (value > Integer.MAX_VALUE) { + throw new HPackException(MAX_LIMIT_EXCEEDED); } + + if ((b & 0x80) == 0) { + return (int) value; + } + m += 7; } + throw new HPackException(MAX_LIMIT_EXCEEDED); } diff --git a/httpcore5-h2/src/test/java/org/apache/hc/core5/http2/hpack/TestHPackCoding.java b/httpcore5-h2/src/test/java/org/apache/hc/core5/http2/hpack/TestHPackCoding.java index de72c0cd9..b93cbd032 100644 --- a/httpcore5-h2/src/test/java/org/apache/hc/core5/http2/hpack/TestHPackCoding.java +++ b/httpcore5-h2/src/test/java/org/apache/hc/core5/http2/hpack/TestHPackCoding.java @@ -1224,5 +1224,14 @@ void decoderRejectsDynamicTableSizeUpdateAfterLeadingUpdateAndHeader() { Assertions.assertTrue(ex.getMessage().contains("beginning of a header block")); } + @Test + void testIntegerDecodingOverflow() { + // 7-bit prefix integer that mathematically decodes to Integer.MAX_VALUE + 127. + // Without an overflow guard this wraps to a negative int and leaks into higher-level decoding. + Assertions.assertThrows(HPackException.class, () -> + HPackDecoder.decodeInt(createByteBuffer(0x7f, 0xff, 0xff, 0xff, 0xff, 0x07), 7)); + } + + }