High severity7.5NVD Advisory· Published Apr 24, 2026· Updated Apr 28, 2026
CVE-2026-33524
CVE-2026-33524
Description
Zserio is a framework for serializing structured data with a compact and efficient way with low overhead. Prior to 2.18.1, a crafted payload as small as 4-5 bytes can force memory allocations of up to 16 GB, crashing any process with an OOM error (Denial of Service). This vulnerability is fixed in 2.18.1.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
io.github.ndsev:zserio-runtimeMaven | < 2.18.1 | 2.18.1 |
Affected products
1Patches
1a9932de4b5eeJava: Implement invalid size checks in ByteArrayBitStreamReader, Array
4 files changed · +109 −7
compiler/extensions/java/runtime/src/zserio/runtime/array/Array.java+9 −0 modified@@ -285,6 +285,15 @@ else if (arrayType == ArrayType.AUTO) readSize = reader.readVarSize(); } + if (arrayTraits.isBitSizeOfConstant()) + { + final int elementSize = arrayTraits.bitSizeOf(reader.getBitPosition(), ArrayElement.Dummy); + if (reader.getBitPosition() + (long)readSize * elementSize > reader.getBufferBitSize()) + { + throw new IOException("Array: Array size exceeds buffer length!"); + } + } + rawArray.reset(readSize); for (int index = 0; index < readSize; ++index)
compiler/extensions/java/runtime/src/zserio/runtime/io/ByteArrayBitStreamReader.java+12 −0 modified@@ -239,6 +239,10 @@ public double readFloat64() throws IOException public byte[] readBytes() throws IOException { final int length = readVarSize(); + if (getBitPosition() + 8L * length > getBufferBitSize()) + { + throw new IOException("ByteArrayBitStreamReader: Byte array size exceeds available buffer!"); + } final byte[] bytesValue = new byte[length]; if (bitOffset != 0) { @@ -259,6 +263,10 @@ public byte[] readBytes() throws IOException public String readString() throws IOException { final int length = readVarSize(); + if (getBitPosition() + 8L * length > getBufferBitSize()) + { + throw new IOException("ByteArrayBitStreamReader: String size exceeds available buffer!"); + } final byte[] readBuffer = new byte[length]; if (bitOffset != 0) { @@ -575,6 +583,10 @@ public int readVarSize() throws IOException public BitBuffer readBitBuffer() throws IOException { final int bitSize = readVarSize(); + if (getBitPosition() + bitSize > getBufferBitSize()) + { + throw new IOException("ByteArrayBitStreamReader: BitBuffer size exceeds available buffer!"); + } final int numBytesToRead = bitSize / 8; final byte numRestBits = (byte)(bitSize - numBytesToRead * 8); final int byteSize = (bitSize + 7) / 8;
compiler/extensions/java/runtime/test/zserio/runtime/array/ArrayTest.java+74 −7 modified@@ -553,7 +553,7 @@ public void signedBitFieldBytePackedArray() throws IOException final RawArray rawArray2 = new RawArray.ByteRawArray(new byte[] {-10, -10, -10, -10, -10, -10, -10}); final int array2BitSizeOf = PACKING_DESCRIPTOR_BITSIZE + elementBitSize; final int array2AlignedBitSizeOf = PACKING_DESCRIPTOR_BITSIZE + elementBitSize + /* alignment */ 4; - testPackedArray(rawArray2, emptyRawArray, arrayTraits, array2BitSizeOf, array2AlignedBitSizeOf); + testPackedArrayConst(rawArray2, emptyRawArray, arrayTraits, array2BitSizeOf, array2AlignedBitSizeOf); // one-element array final RawArray rawArray3 = new RawArray.ByteRawArray(new byte[] {-10}); @@ -637,7 +637,7 @@ public void bitFieldBytePackedArray() throws IOException final RawArray rawArray2 = new RawArray.ByteRawArray(new byte[] {1, 1, 1, 1, 1, 1, 1}); final int array2BitSizeOf = PACKING_DESCRIPTOR_BITSIZE + elementBitSize; final int array2AlignedBitSizeOf = PACKING_DESCRIPTOR_BITSIZE + elementBitSize + /* alignment */ 4; - testPackedArray(rawArray2, emptyRawArray, arrayTraits, array2BitSizeOf, array2AlignedBitSizeOf); + testPackedArrayConst(rawArray2, emptyRawArray, arrayTraits, array2BitSizeOf, array2AlignedBitSizeOf); // one-element array final RawArray rawArray3 = new RawArray.ByteRawArray(new byte[] {1}); @@ -1035,14 +1035,32 @@ private static void testArrayNormal(RawArray rawArray, RawArray readRawArray, Ar final Array readArray = new Array(readRawArray, arrayTraits, ArrayType.NORMAL); readArray.read(reader, rawArray.size()); assertEquals(array, readArray); + + if (rawArray.size() > 0) + { + reader.setBitPosition(bitPosition); + assertThrows(IOException.class, () -> readArray.read(reader, 10 * rawArray.size())); + } } } } } + private static void testInvalidArrayLength(RawArray readRawArray, ArrayTraits arrayTraits) + throws IOException + { + final byte[] data = {(byte)0xff, (byte)0xff, (byte)0xff, 0x3f, 0}; + try (final ByteArrayBitStreamReader reader = new ByteArrayBitStreamReader(data)) + { + final Array array = new Array(readRawArray, arrayTraits, ArrayType.AUTO); + assertThrows(Exception.class, () -> array.read(reader)); + } + } + private static void testArrayAuto(RawArray rawArray, RawArray readRawArray, ArrayTraits arrayTraits, int expectedBitSizeOf) throws IOException { + testInvalidArrayLength(readRawArray, arrayTraits); final Array array = new Array(rawArray, arrayTraits, ArrayType.AUTO); for (int bitPosition = 0; bitPosition < 8; ++bitPosition) { @@ -1147,6 +1165,12 @@ private static void testArrayAligned(RawArray rawArray, RawArray readRawArray, A readRawArray, arrayTraits, ArrayType.NORMAL, offsetChecker, offsetInitializer); readArray.read(reader, rawArray.size()); assertEquals(array, readArray); + + if (rawArray.size() > 0) + { + reader.setBitPosition(bitPosition); + assertThrows(IOException.class, () -> readArray.read(reader, 10 * rawArray.size())); + } } } } @@ -1156,6 +1180,7 @@ private static void testArrayAlignedAuto(RawArray rawArray, RawArray readRawArra OffsetChecker offsetChecker, OffsetInitializer offsetInitializer, int expectedBitSizeOf) throws IOException { + testInvalidArrayLength(readRawArray, arrayTraits); final Array array = new Array(rawArray, arrayTraits, ArrayType.AUTO, offsetChecker, offsetInitializer); for (int bitPosition = 0; bitPosition < 8; ++bitPosition) { @@ -1200,7 +1225,19 @@ private static void testPackedArray(RawArray rawArray, RawArray emptyRawArray, A private static void testPackedArray(RawArray rawArray, RawArray readRawArray, ArrayTraits arrayTraits, int bitSizeOf, int alignedBitSizeOf) throws IOException { - testPackedArrayNormal(rawArray, readRawArray, arrayTraits, bitSizeOf); + testPackedArray(rawArray, readRawArray, arrayTraits, bitSizeOf, alignedBitSizeOf, false); + } + + private static void testPackedArrayConst(RawArray rawArray, RawArray readRawArray, ArrayTraits arrayTraits, + int bitSizeOf, int alignedBitSizeOf) throws IOException + { + testPackedArray(rawArray, readRawArray, arrayTraits, bitSizeOf, alignedBitSizeOf, true); + } + + private static void testPackedArray(RawArray rawArray, RawArray readRawArray, ArrayTraits arrayTraits, + int bitSizeOf, int alignedBitSizeOf, boolean equalElements) throws IOException + { + testPackedArrayNormal(rawArray, readRawArray, arrayTraits, bitSizeOf, equalElements); final int autoSizeBitSizeOf = BitSizeOfCalculator.getBitSizeOfVarSize(rawArray.size()); final int autoBitSizeOf = (bitSizeOf != UNKNOWN_BITSIZE) ? autoSizeBitSizeOf + bitSizeOf : UNKNOWN_BITSIZE; @@ -1209,16 +1246,16 @@ private static void testPackedArray(RawArray rawArray, RawArray readRawArray, Ar final OffsetChecker offsetChecker = new ArrayTestOffsetChecker(); final OffsetInitializer offsetInitializer = new ArrayTestOffsetInitializer(); - testPackedArrayAligned( - rawArray, readRawArray, arrayTraits, offsetChecker, offsetInitializer, alignedBitSizeOf); + testPackedArrayAligned(rawArray, readRawArray, arrayTraits, offsetChecker, offsetInitializer, + alignedBitSizeOf, equalElements); final int autoAlignedBitSizeOf = (alignedBitSizeOf != UNKNOWN_BITSIZE) ? autoSizeBitSizeOf + alignedBitSizeOf : UNKNOWN_BITSIZE; testPackedArrayAlignedAuto( rawArray, readRawArray, arrayTraits, offsetChecker, offsetInitializer, autoAlignedBitSizeOf); } private static void testPackedArrayNormal(RawArray rawArray, RawArray readRawArray, ArrayTraits arrayTraits, - int expectedBitSizeOf) throws IOException + int expectedBitSizeOf, boolean equalElements) throws IOException { final Array array = new Array(rawArray, arrayTraits, ArrayType.NORMAL); for (int bitPosition = 0; bitPosition < 8; ++bitPosition) @@ -1245,14 +1282,35 @@ private static void testPackedArrayNormal(RawArray rawArray, RawArray readRawArr final Array readArray = new Array(readRawArray, arrayTraits, ArrayType.NORMAL); readArray.readPacked(reader, rawArray.size()); assertEquals(array, readArray); + + if (rawArray.size() > 0 && !equalElements) + { + // equal elements are packed into 0 bits so unlimited read is possible + reader.setBitPosition(bitPosition); + assertThrows( + IOException.class, () -> readArray.readPacked(reader, 10 * rawArray.size())); + } } } } } + private static void testPackedInvalidArrayLength(RawArray readRawArray, ArrayTraits arrayTraits) + throws IOException + { + final byte[] data = {(byte)0xff, (byte)0xff, (byte)0xff, 0x3f, 0}; + try (final ByteArrayBitStreamReader reader = new ByteArrayBitStreamReader(data)) + { + final Array array = new Array(readRawArray, arrayTraits, ArrayType.AUTO); + // can throw IOException, EOFException, ArrayOutOfBoundsException + assertThrows(Exception.class, () -> array.readPacked(reader)); + } + } + private static void testPackedArrayAuto(RawArray rawArray, RawArray readRawArray, ArrayTraits arrayTraits, int expectedBitSizeOf) throws IOException { + testPackedInvalidArrayLength(readRawArray, arrayTraits); final Array array = new Array(rawArray, arrayTraits, ArrayType.AUTO); for (int bitPosition = 0; bitPosition < 8; ++bitPosition) { @@ -1305,7 +1363,7 @@ private static void testPackedArrayImplicit( private static void testPackedArrayAligned(RawArray rawArray, RawArray readRawArray, ArrayTraits arrayTraits, OffsetChecker offsetChecker, OffsetInitializer offsetInitializer, - int expectedBitSizeOf) throws IOException + int expectedBitSizeOf, boolean equalElements) throws IOException { final Array array = new Array(rawArray, arrayTraits, ArrayType.NORMAL, offsetChecker, offsetInitializer); @@ -1334,6 +1392,14 @@ private static void testPackedArrayAligned(RawArray rawArray, RawArray readRawAr readRawArray, arrayTraits, ArrayType.NORMAL, offsetChecker, offsetInitializer); readArray.readPacked(reader, rawArray.size()); assertEquals(array, readArray); + + if (rawArray.size() > 0 && !equalElements) + { + // equal elements pack into 0 bits so unlimited read is possible + reader.setBitPosition(bitPosition); + assertThrows( + IOException.class, () -> readArray.readPacked(reader, 10 * rawArray.size())); + } } } } @@ -1343,6 +1409,7 @@ private static void testPackedArrayAlignedAuto(RawArray rawArray, RawArray readR ArrayTraits arrayTraits, OffsetChecker offsetChecker, OffsetInitializer offsetInitializer, int expectedBitSizeOf) throws IOException { + testPackedInvalidArrayLength(readRawArray, arrayTraits); final Array array = new Array(rawArray, arrayTraits, ArrayType.AUTO, offsetChecker, offsetInitializer); for (int bitPosition = 0; bitPosition < 8; ++bitPosition) {
compiler/extensions/java/runtime/test/zserio/runtime/io/ByteArrayBitStreamReaderTest.java+14 −0 modified@@ -723,6 +723,20 @@ public void readUnaligned63Bits() throws IOException } } + @Test + public void invalidVarSize() throws IOException + { + final byte[] data = {127, 0, 0, 0, 0}; + try (final ByteArrayBitStreamReader reader = new ByteArrayBitStreamReader(data)) + { + assertThrows(IOException.class, () -> reader.readBytes()); + reader.setBitPosition(0); + assertThrows(IOException.class, () -> reader.readString()); + reader.setBitPosition(0); + assertThrows(IOException.class, () -> reader.readBitBuffer()); + } + } + private interface WriteReadTestable { // don't use BitStreamWriter so that this tests solely the reader
Vulnerability mechanics
Generated by null/stub on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
4- github.com/ndsev/zserio/security/advisories/GHSA-cwq5-8pvq-j65jnvdExploitMitigationVendor AdvisoryWEB
- github.com/advisories/GHSA-cwq5-8pvq-j65jghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2026-33524ghsaADVISORY
- github.com/ndsev/zserio/commit/a9932de4b5eefb3afd5e18ca2fd758aa744a7c69ghsaWEB
News mentions
0No linked articles in our index yet.