Check for long streams in IOUtils.toByteArray
MODIFY
src/changes/changes.xml
src/changes/changes.xml
@@ -126,6 +126,9 @@ The <action> type attribute can be add,update,fix,remove.
<action dev="ggregory" type="fix" due-to="Rob Spoor, Gary Gregory">
Prevent infinite loop with AbstractCharacterFilterReader if EOF is filtered out #226.
</action>
+ <action issue="IO-429" dev="ggregory" type="fix" due-to="Rob Spoor, Ivan Leskin">
+ Check for long streams in IOUtils.toByteArray #175.
+ </action>
<!-- ADD -->
<action dev="ggregory" type="add" due-to="Gary Gregory">
Add FileSystemProviders class.

MODIFY
src/main/java/org/apache/commons/io/IOUtils.java
src/main/java/org/apache/commons/io/IOUtils.java
import org.apache.commons.io.output.ByteArrayOutputStream;
import org.apache.commons.io.output.NullOutputStream;
import org.apache.commons.io.output.StringBuilderWriter;
+import org.apache.commons.io.output.ThresholdingOutputStream;
import org.apache.commons.io.output.UnsynchronizedByteArrayOutputStream;
/**
@@ -2394,12 +2395,17 @@ public static BufferedReader toBufferedReader(final Reader reader, final int siz
* @param inputStream the {@code InputStream} to read.
* @return the requested byte array.
* @throws NullPointerException if the InputStream is {@code null}.
- * @throws IOException if an I/O error occurs.
+ * @throws IOException if an I/O error occurs or reading more than {@link Integer#MAX_VALUE} occurs.
*/
public static byte[] toByteArray(final InputStream inputStream) throws IOException {
- try (final UnsynchronizedByteArrayOutputStream output = new UnsynchronizedByteArrayOutputStream()) {
- copy(inputStream, output);
- return output.toByteArray();
+ // We use a ThresholdingOutputStream to avoid reading AND writing more than Integer.MAX_VALUE.
+ try (final UnsynchronizedByteArrayOutputStream ubaOutput = new UnsynchronizedByteArrayOutputStream();
+ final ThresholdingOutputStream thresholdOuput = new ThresholdingOutputStream(Integer.MAX_VALUE, os -> {
+ throw new IllegalArgumentException(
+ String.format("Cannot read more than %,d into a byte array", Integer.MAX_VALUE));
+ }, os -> ubaOutput)) {
+ copy(inputStream, thresholdOuput);
+ return ubaOutput.toByteArray();
}
}

MODIFY
src/test/java/org/apache/commons/io/IOUtilsTestCase.java
src/test/java/org/apache/commons/io/IOUtilsTestCase.java
import java.util.List;
import org.apache.commons.io.function.IOConsumer;
+import org.apache.commons.io.input.CircularInputStream;
import org.apache.commons.io.input.NullInputStream;
import org.apache.commons.io.output.AppendableWriter;
import org.apache.commons.io.output.NullOutputStream;
import org.apache.commons.io.test.TestUtils;
import org.apache.commons.io.test.ThrowOnCloseReader;
import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;
@@ -1435,6 +1437,13 @@ public void testToByteArray_InputStream() throws Exception {
}
}
+ @Test
+ @Disabled("Disable by default as it uses too much memory and can cause builds to fail.")
+ public void testToByteArray_InputStream_LongerThanIntegerMaxValue() throws Exception {
+ final CircularInputStream cin = new CircularInputStream(IOUtils.byteArray(), Integer.MAX_VALUE + 1L);
+ assertThrows(IllegalArgumentException.class, () -> IOUtils.toByteArray(cin));
+ }
+
@Test
public void testToByteArray_InputStream_NegativeSize() throws Exception {

[IO-429] Check for long streams in IOUtils.toByteArray #175.