diff --git a/CHANGELOG.md b/CHANGELOG.md index 320805a55..46e09b451 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -41,6 +41,8 @@ ### Bug Fixes +- **[client-v2]** Fixed binary varint decoding for length and count fields so overflowing or overlong values fail with an `IOException` instead of being decoded into corrupted or negative `int` values. + - **[jdbc-v2]** Fixed `Statement.cancel()` throwing `SESSION_IS_LOCKED` when the statement was running inside a ClickHouse session. The driver now accepts `session_id`, `session_check`, and `session_timeout` as first-class connection properties and correctly suppresses them when issuing a `KILL QUERY` during cancellation. This ensures the cancellation request runs outside the session and no longer contends with the running query for the session lock. (https://github.com/ClickHouse/clickhouse-java/issues/2690, https://github.com/ClickHouse/clickhouse-java/issues/2881) - **[client-v2]** Fixed inconsistent use of `executionTimeout` parameter in `Client` component. The timeout was previously set in milliseconds but mistakenly retrieved and used in seconds in some places. Now it correctly uses milliseconds consistently. (https://github.com/ClickHouse/clickhouse-java/issues/2358) diff --git a/client-v2/src/main/java/com/clickhouse/client/api/data_formats/internal/BinaryStreamReader.java b/client-v2/src/main/java/com/clickhouse/client/api/data_formats/internal/BinaryStreamReader.java index 8a6b76a5a..8077bb326 100644 --- a/client-v2/src/main/java/com/clickhouse/client/api/data_formats/internal/BinaryStreamReader.java +++ b/client-v2/src/main/java/com/clickhouse/client/api/data_formats/internal/BinaryStreamReader.java @@ -959,16 +959,20 @@ private double[][][][] readGeoMultiPolygon() throws IOException { public static int readVarInt(InputStream input) throws IOException { int value = 0; - for (int i = 0; i < 10; i++) { + for (int i = 0; i < 5; i++) { byte b = (byte) readByteOrEOF(input); + if (i == 4 && (b & 0xF8) != 0) { + throw new IOException("VarInt is too large for int"); + } + value |= (b & 0x7F) << (7 * i); if ((b & 0x80) == 0) { - break; + return value; } } - return value; + throw new IOException("Malformed VarInt"); } /** diff --git a/client-v2/src/test/java/com/clickhouse/client/api/data_formats/internal/BinaryStreamReaderTests.java b/client-v2/src/test/java/com/clickhouse/client/api/data_formats/internal/BinaryStreamReaderTests.java index 0d94e0a5f..2852d63df 100644 --- a/client-v2/src/test/java/com/clickhouse/client/api/data_formats/internal/BinaryStreamReaderTests.java +++ b/client-v2/src/test/java/com/clickhouse/client/api/data_formats/internal/BinaryStreamReaderTests.java @@ -204,4 +204,26 @@ public void testReadNullVariantReturnsNull() throws Exception { Assert.assertNull(reader.readValue(column)); } + + @Test + public void testReadVarIntReadsMaxInt() throws IOException { + Assert.assertEquals(readVarInt((byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0x07), + Integer.MAX_VALUE); + } + + @Test + public void testReadVarIntRejectsOverflow() { + Assert.assertThrows(IOException.class, + () -> readVarInt((byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x08)); + } + + @Test + public void testReadVarIntRejectsOverlongValue() { + Assert.assertThrows(IOException.class, + () -> readVarInt((byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x01)); + } + + private static int readVarInt(byte... bytes) throws IOException { + return BinaryStreamReader.readVarInt(new ByteArrayInputStream(bytes)); + } }