diff --git a/src/main/java/org/apache/commons/collections4/queue/CircularFifoQueue.java b/src/main/java/org/apache/commons/collections4/queue/CircularFifoQueue.java index 3988666e7d..9417583e34 100644 --- a/src/main/java/org/apache/commons/collections4/queue/CircularFifoQueue.java +++ b/src/main/java/org/apache/commons/collections4/queue/CircularFifoQueue.java @@ -17,6 +17,7 @@ package org.apache.commons.collections4.queue; import java.io.IOException; +import java.io.InvalidObjectException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; @@ -358,8 +359,14 @@ public E poll() { @SuppressWarnings("unchecked") private void readObject(final ObjectInputStream in) throws IOException, ClassNotFoundException { in.defaultReadObject(); + if (maxElements < 1) { + throw new InvalidObjectException("maxElements must be greater than 0"); + } elements = (E[]) new Object[maxElements]; final int size = in.readInt(); + if (size < 0 || size > maxElements) { + throw new InvalidObjectException("size is out of range: " + size); + } for (int i = 0; i < size; i++) { elements[i] = (E) in.readObject(); } diff --git a/src/test/java/org/apache/commons/collections4/queue/CircularFifoQueueTest.java b/src/test/java/org/apache/commons/collections4/queue/CircularFifoQueueTest.java index fca917e228..20df0a8acf 100644 --- a/src/test/java/org/apache/commons/collections4/queue/CircularFifoQueueTest.java +++ b/src/test/java/org/apache/commons/collections4/queue/CircularFifoQueueTest.java @@ -21,6 +21,7 @@ import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; +import java.io.InvalidObjectException; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; @@ -428,6 +429,46 @@ void testRepeatedSerialization() throws Exception { assertTrue(b3.contains("c")); } + @Test + @SuppressWarnings("unchecked") + void testDeserializeRejectsCorruptSize() throws Exception { + // a stored size larger than maxElements would write past the backing array + final CircularFifoQueue full = new CircularFifoQueue<>(7); + for (int i = 0; i < 7; i++) { + full.add((E) ("x" + i)); + } + final byte[] tooLarge = serialize(full); + // first 0x00000007 is maxElements; shrink it so size (7) now exceeds it + patchInt(tooLarge, indexOfInt(tooLarge, 7), 2); + assertThrows(InvalidObjectException.class, () -> deserialize(tooLarge)); + + // a negative stored size leaves the queue in an inconsistent state + final CircularFifoQueue partial = new CircularFifoQueue<>(7); + partial.add((E) "a"); + partial.add((E) "b"); + final byte[] negative = serialize(partial); + patchInt(negative, indexOfInt(negative, 2), -1); + assertThrows(InvalidObjectException.class, () -> deserialize(negative)); + } + + private static int indexOfInt(final byte[] data, final int value) { + final byte[] needle = {(byte) (value >>> 24), (byte) (value >>> 16), (byte) (value >>> 8), (byte) value}; + for (int i = 0; i <= data.length - 4; i++) { + if (data[i] == needle[0] && data[i + 1] == needle[1] + && data[i + 2] == needle[2] && data[i + 3] == needle[3]) { + return i; + } + } + throw new IllegalStateException("value not found in stream"); + } + + private static void patchInt(final byte[] data, final int pos, final int value) { + data[pos] = (byte) (value >>> 24); + data[pos + 1] = (byte) (value >>> 16); + data[pos + 2] = (byte) (value >>> 8); + data[pos + 3] = (byte) value; + } + // void testCreate() throws Exception { // resetEmpty(); // writeExternalFormToDisk((java.io.Serializable) getCollection(), "src/test/resources/data/test/CircularFifoQueue.emptyCollection.version4.obj");