VYPR
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.

PackageAffected versionsPatched versions
io.github.ndsev:zserio-runtimeMaven
< 2.18.12.18.1

Affected products

1

Patches

1
a9932de4b5ee

Java: Implement invalid size checks in ByteArrayBitStreamReader, Array

https://github.com/ndsev/zserioTomas PecholtApr 7, 2026via ghsa
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

News mentions

0

No linked articles in our index yet.