aboutsummaryrefslogtreecommitdiffstats
path: root/src/java
diff options
context:
space:
mode:
authorAndreas Beeker <kiwiwings@apache.org>2018-04-27 21:38:19 +0000
committerAndreas Beeker <kiwiwings@apache.org>2018-04-27 21:38:19 +0000
commitf94245e9d876c49462bc66bdc573ea11160b617a (patch)
treeb3ad7edff8643e1fdb024a568f1e071f98232537 /src/java
parent48f03cd45abcef0ca26e91e2080f430557a2c70b (diff)
downloadpoi-f94245e9d876c49462bc66bdc573ea11160b617a.tar.gz
poi-f94245e9d876c49462bc66bdc573ea11160b617a.zip
#59893 - Forbid calls to InputStream.available
git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1830400 13f79535-47bb-0310-9956-ffa450edef68
Diffstat (limited to 'src/java')
-rw-r--r--src/java/org/apache/poi/hpsf/VariantSupport.java10
-rw-r--r--src/java/org/apache/poi/hssf/record/ObjRecord.java8
-rw-r--r--src/java/org/apache/poi/hssf/record/crypto/Biff8DecryptingStream.java8
-rw-r--r--src/java/org/apache/poi/poifs/crypt/ChunkedCipherInputStream.java14
-rw-r--r--src/java/org/apache/poi/poifs/eventfilesystem/POIFSReader.java16
-rw-r--r--src/java/org/apache/poi/poifs/filesystem/ODocumentInputStream.java2
-rw-r--r--src/java/org/apache/poi/sl/draw/BitmapImageRenderer.java40
-rw-r--r--src/java/org/apache/poi/util/IOUtils.java74
8 files changed, 100 insertions, 72 deletions
diff --git a/src/java/org/apache/poi/hpsf/VariantSupport.java b/src/java/org/apache/poi/hpsf/VariantSupport.java
index 7d57cbb5a6..e60679027d 100644
--- a/src/java/org/apache/poi/hpsf/VariantSupport.java
+++ b/src/java/org/apache/poi/hpsf/VariantSupport.java
@@ -175,10 +175,12 @@ public class VariantSupport extends Variant {
try {
typedPropertyValue.readValue(lei);
} catch ( UnsupportedOperationException exc ) {
- int propLength = Math.min( length, lei.available() );
- final byte[] v = IOUtils.safelyAllocate(propLength, MAX_RECORD_LENGTH);
- lei.readFully(v, 0, propLength);
- throw new ReadingNotSupportedException( type, v );
+ try {
+ final byte[] v = IOUtils.toByteArray(lei, length, MAX_RECORD_LENGTH);
+ throw new ReadingNotSupportedException( type, v );
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
}
switch ( (int) type ) {
diff --git a/src/java/org/apache/poi/hssf/record/ObjRecord.java b/src/java/org/apache/poi/hssf/record/ObjRecord.java
index e41e6cbc27..d1b4ab4d11 100644
--- a/src/java/org/apache/poi/hssf/record/ObjRecord.java
+++ b/src/java/org/apache/poi/hssf/record/ObjRecord.java
@@ -17,14 +17,13 @@
package org.apache.poi.hssf.record;
-import java.io.ByteArrayInputStream;
import java.util.ArrayList;
import java.util.List;
import org.apache.poi.util.HexDump;
import org.apache.poi.util.LittleEndian;
+import org.apache.poi.util.LittleEndianByteArrayInputStream;
import org.apache.poi.util.LittleEndianByteArrayOutputStream;
-import org.apache.poi.util.LittleEndianInputStream;
import org.apache.poi.util.RecordFormatException;
/**
@@ -85,8 +84,7 @@ public final class ObjRecord extends Record implements Cloneable {
*/
subrecords = new ArrayList<>();
- ByteArrayInputStream bais = new ByteArrayInputStream(subRecordData);
- LittleEndianInputStream subRecStream = new LittleEndianInputStream(bais);
+ LittleEndianByteArrayInputStream subRecStream = new LittleEndianByteArrayInputStream(subRecordData);
CommonObjectDataSubRecord cmo = (CommonObjectDataSubRecord)SubRecord.createSubRecord(subRecStream, 0);
subrecords.add(cmo);
while (true) {
@@ -96,7 +94,7 @@ public final class ObjRecord extends Record implements Cloneable {
break;
}
}
- int nRemainingBytes = bais.available();
+ final int nRemainingBytes = subRecordData.length-subRecStream.getReadIndex();
if (nRemainingBytes > 0) {
// At present (Oct-2008), most unit test samples have (subRecordData.length % 2 == 0)
_isPaddedToQuadByteMultiple = subRecordData.length % MAX_PAD_ALIGNMENT == 0;
diff --git a/src/java/org/apache/poi/hssf/record/crypto/Biff8DecryptingStream.java b/src/java/org/apache/poi/hssf/record/crypto/Biff8DecryptingStream.java
index 3c5be4267c..99c5d46dc9 100644
--- a/src/java/org/apache/poi/hssf/record/crypto/Biff8DecryptingStream.java
+++ b/src/java/org/apache/poi/hssf/record/crypto/Biff8DecryptingStream.java
@@ -32,6 +32,7 @@ import org.apache.poi.util.LittleEndian;
import org.apache.poi.util.LittleEndianConsts;
import org.apache.poi.util.LittleEndianInput;
import org.apache.poi.util.RecordFormatException;
+import org.apache.poi.util.SuppressForbidden;
public final class Biff8DecryptingStream implements BiffHeaderInput, LittleEndianInput {
@@ -39,7 +40,6 @@ public final class Biff8DecryptingStream implements BiffHeaderInput, LittleEndia
//arbitrarily selected; may need to increase
private static final int MAX_RECORD_LENGTH = 100_000;
- private final EncryptionInfo info;
private ChunkedCipherInputStream ccis;
private final byte buffer[] = new byte[LittleEndianConsts.LONG_SIZE];
private boolean shouldSkipEncryptionOnCurrentRecord;
@@ -54,9 +54,8 @@ public final class Biff8DecryptingStream implements BiffHeaderInput, LittleEndia
stream = new PushbackInputStream(in, initialOffset);
((PushbackInputStream)stream).unread(initialBuf);
}
-
- this.info = info;
- Decryptor dec = this.info.getDecryptor();
+
+ Decryptor dec = info.getDecryptor();
dec.setChunkSize(RC4_REKEYING_INTERVAL);
ccis = (ChunkedCipherInputStream)dec.getDataStream(stream, Integer.MAX_VALUE, 0);
@@ -69,6 +68,7 @@ public final class Biff8DecryptingStream implements BiffHeaderInput, LittleEndia
}
@Override
+ @SuppressForbidden("just delegating")
public int available() {
return ccis.available();
}
diff --git a/src/java/org/apache/poi/poifs/crypt/ChunkedCipherInputStream.java b/src/java/org/apache/poi/poifs/crypt/ChunkedCipherInputStream.java
index 77a967d1df..0b84362670 100644
--- a/src/java/org/apache/poi/poifs/crypt/ChunkedCipherInputStream.java
+++ b/src/java/org/apache/poi/poifs/crypt/ChunkedCipherInputStream.java
@@ -97,7 +97,7 @@ public abstract class ChunkedCipherInputStream extends LittleEndianInputStream {
private int read(byte[] b, int off, int len, boolean readPlain) throws IOException {
int total = 0;
- if (available() <= 0) {
+ if (remainingBytes() <= 0) {
return -1;
}
@@ -112,7 +112,7 @@ public abstract class ChunkedCipherInputStream extends LittleEndianInputStream {
}
}
int count = (int)(chunk.length - (pos & chunkMask));
- int avail = available();
+ int avail = remainingBytes();
if (avail == 0) {
return total;
}
@@ -133,7 +133,7 @@ public abstract class ChunkedCipherInputStream extends LittleEndianInputStream {
}
@Override
- public long skip(final long n) throws IOException {
+ public long skip(final long n) {
long start = pos;
long skip = Math.min(remainingBytes(), n);
@@ -169,7 +169,7 @@ public abstract class ChunkedCipherInputStream extends LittleEndianInputStream {
}
@Override
- public synchronized void reset() throws IOException {
+ public synchronized void reset() {
throw new UnsupportedOperationException();
}
@@ -193,7 +193,7 @@ public abstract class ChunkedCipherInputStream extends LittleEndianInputStream {
}
final int todo = (int)Math.min(size, chunk.length);
- int readBytes = 0, totalBytes = 0;
+ int readBytes, totalBytes = 0;
do {
readBytes = super.read(plain, totalBytes, todo-totalBytes);
totalBytes += Math.max(0, readBytes);
@@ -211,10 +211,6 @@ public abstract class ChunkedCipherInputStream extends LittleEndianInputStream {
/**
* Helper function for overriding the cipher invocation, i.e. XOR doesn't use a cipher
* and uses it's own implementation
- *
- * @throws BadPaddingException
- * @throws IllegalBlockSizeException
- * @throws ShortBufferException
*/
protected int invokeCipher(int totalBytes, boolean doFinal) throws GeneralSecurityException {
if (doFinal) {
diff --git a/src/java/org/apache/poi/poifs/eventfilesystem/POIFSReader.java b/src/java/org/apache/poi/poifs/eventfilesystem/POIFSReader.java
index c4aaecfe0c..e61039360d 100644
--- a/src/java/org/apache/poi/poifs/eventfilesystem/POIFSReader.java
+++ b/src/java/org/apache/poi/poifs/eventfilesystem/POIFSReader.java
@@ -184,15 +184,15 @@ public class POIFSReader
/**
* Activates the notification of empty directories.<p>
* If this flag is activated, the {@link POIFSReaderListener listener} receives
- * {@link POIFSReaderEvent POIFSReaderEvents} with nulled {@code name} and {@code stream}
+ * {@link POIFSReaderEvent POIFSReaderEvents} with nulled {@code name} and {@code stream}
*
* @param notifyEmptyDirectories
*/
public void setNotifyEmptyDirectories(boolean notifyEmptyDirectories) {
this.notifyEmptyDirectories = notifyEmptyDirectories;
}
-
-
+
+
/**
* read in files
*
@@ -239,7 +239,7 @@ public class POIFSReader
}
return;
}
-
+
while (properties.hasNext())
{
Property property = properties.next();
@@ -273,11 +273,9 @@ public class POIFSReader
while (listeners.hasNext())
{
POIFSReaderListener listener = listeners.next();
-
- listener.processPOIFSReaderEvent(
- new POIFSReaderEvent(
- new DocumentInputStream(document), path,
- name));
+ try (DocumentInputStream dis = new DocumentInputStream(document)) {
+ listener.processPOIFSReaderEvent(new POIFSReaderEvent(dis, path, name));
+ }
}
}
else
diff --git a/src/java/org/apache/poi/poifs/filesystem/ODocumentInputStream.java b/src/java/org/apache/poi/poifs/filesystem/ODocumentInputStream.java
index cc280390ef..97225805a4 100644
--- a/src/java/org/apache/poi/poifs/filesystem/ODocumentInputStream.java
+++ b/src/java/org/apache/poi/poifs/filesystem/ODocumentInputStream.java
@@ -138,7 +138,7 @@ public final class ODocumentInputStream extends DocumentInputStream {
if (atEOD()) {
return EOF;
}
- int limit = Math.min(available(), len);
+ int limit = Math.min(_document_size - _current_offset, len);
readFully(b, off, limit);
return limit;
}
diff --git a/src/java/org/apache/poi/sl/draw/BitmapImageRenderer.java b/src/java/org/apache/poi/sl/draw/BitmapImageRenderer.java
index e09054479f..a92c8dc590 100644
--- a/src/java/org/apache/poi/sl/draw/BitmapImageRenderer.java
+++ b/src/java/org/apache/poi/sl/draw/BitmapImageRenderer.java
@@ -28,6 +28,7 @@ import java.awt.image.AffineTransformOp;
import java.awt.image.BufferedImage;
import java.awt.image.RescaleOp;
import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Iterator;
@@ -39,6 +40,7 @@ import javax.imageio.ImageTypeSpecifier;
import javax.imageio.stream.ImageInputStream;
import javax.imageio.stream.MemoryCacheImageInputStream;
+import org.apache.poi.util.IOUtils;
import org.apache.poi.util.POILogFactory;
import org.apache.poi.util.POILogger;
@@ -69,20 +71,24 @@ public class BitmapImageRenderer implements ImageRenderer {
* @return the bufferedImage or null, if there was no image reader for this content type
* @throws IOException thrown if there was an error while processing the image
*/
- private static BufferedImage readImage(InputStream data, String contentType) throws IOException {
+ private static BufferedImage readImage(final InputStream data, final String contentType) throws IOException {
IOException lastException = null;
BufferedImage img = null;
- if (data.markSupported()) {
- data.mark(data.available());
+
+ final ByteArrayInputStream bis;
+ if (data instanceof ByteArrayInputStream) {
+ bis = (ByteArrayInputStream)data;
+ } else {
+ ByteArrayOutputStream bos = new ByteArrayOutputStream(0x3FFFF);
+ IOUtils.copy(data, bos);
+ bis = new ByteArrayInputStream(bos.toByteArray());
}
-
+
+
// currently don't use FileCacheImageInputStream,
// because of the risk of filling the file handles (see #59166)
- ImageInputStream iis = new MemoryCacheImageInputStream(data);
+ ImageInputStream iis = new MemoryCacheImageInputStream(bis);
try {
- iis = new MemoryCacheImageInputStream(data);
- iis.mark();
-
Iterator<ImageReader> iter = ImageIO.getImageReaders(iis);
while (img==null && iter.hasNext()) {
ImageReader reader = iter.next();
@@ -90,21 +96,11 @@ public class BitmapImageRenderer implements ImageRenderer {
// 0:default mode, 1:fallback mode
for (int mode=0; img==null && mode<3; mode++) {
lastException = null;
- try {
- iis.reset();
- } catch (IOException e) {
- if (data.markSupported()) {
- data.reset();
- data.mark(data.available());
- iis.close();
- iis = new MemoryCacheImageInputStream(data);
- } else {
- // can't restore the input stream, so we need to stop processing here
- lastException = e;
- break;
- }
+ if (mode > 0) {
+ bis.reset();
+ iis.close();
+ iis = new MemoryCacheImageInputStream(bis);
}
- iis.mark();
try {
diff --git a/src/java/org/apache/poi/util/IOUtils.java b/src/java/org/apache/poi/util/IOUtils.java
index 0fbe0df46c..c6bf8bd2ef 100644
--- a/src/java/org/apache/poi/util/IOUtils.java
+++ b/src/java/org/apache/poi/util/IOUtils.java
@@ -68,7 +68,7 @@ public final class IOUtils {
/**
* Peeks at the first N bytes of the stream. Returns those bytes, but
* with the stream unaffected. Requires a stream that supports mark/reset,
- * or a PushbackInputStream. If the stream has &gt;0 but &lt;N bytes,
+ * or a PushbackInputStream. If the stream has &gt;0 but &lt;N bytes,
* remaining bytes will be zero.
* @throws EmptyFileException if the stream is empty
*/
@@ -81,7 +81,7 @@ public final class IOUtils {
if (readBytes == 0) {
throw new EmptyFileException();
}
-
+
if (readBytes < limit) {
bos.write(new byte[limit-readBytes]);
}
@@ -116,23 +116,59 @@ public final class IOUtils {
* @return A byte array with the read bytes.
* @throws IOException If reading data fails or EOF is encountered too early for the given length.
*/
- public static byte[] toByteArray(InputStream stream, int length) throws IOException {
- ByteArrayOutputStream baos = new ByteArrayOutputStream(length == Integer.MAX_VALUE ? 4096 : length);
+ public static byte[] toByteArray(InputStream stream, final int length) throws IOException {
+ return toByteArray(stream, length, Integer.MAX_VALUE);
+ }
+
+
+ /**
+ * Reads up to {@code length} bytes from the input stream, and returns the bytes read.
+ *
+ * @param stream The byte stream of data to read.
+ * @param length The maximum length to read, use {@link Integer#MAX_VALUE} to read the stream
+ * until EOF
+ * @param maxLength if the input is equal to/longer than {@code maxLength} bytes,
+ * then throw an {@link IOException} complaining about the length.
+* use {@link Integer#MAX_VALUE} to disable the check
+ * @return A byte array with the read bytes.
+ * @throws IOException If reading data fails or EOF is encountered too early for the given length.
+ */
+ public static byte[] toByteArray(InputStream stream, final long length, final int maxLength) throws IOException {
+ if (length < 0L || maxLength < 0L) {
+ throw new RecordFormatException("Can't allocate an array of length < 0");
+ }
+ if (length > (long)Integer.MAX_VALUE) {
+ throw new RecordFormatException("Can't allocate an array > "+Integer.MAX_VALUE);
+ }
+ if (BYTE_ARRAY_MAX_OVERRIDE > 0) {
+ if (length > BYTE_ARRAY_MAX_OVERRIDE) {
+ throwRFE(length, BYTE_ARRAY_MAX_OVERRIDE);
+ }
+ } else if (length > maxLength) {
+ throwRFE(length, maxLength);
+ }
+
+ final int len = Math.min((int)length, maxLength);
+ ByteArrayOutputStream baos = new ByteArrayOutputStream(len == Integer.MAX_VALUE ? 4096 : len);
byte[] buffer = new byte[4096];
int totalBytes = 0, readBytes;
do {
- readBytes = stream.read(buffer, 0, Math.min(buffer.length, length-totalBytes));
+ readBytes = stream.read(buffer, 0, Math.min(buffer.length, len-totalBytes));
totalBytes += Math.max(readBytes,0);
if (readBytes > 0) {
baos.write(buffer, 0, readBytes);
}
- } while (totalBytes < length && readBytes > -1);
+ } while (totalBytes < len && readBytes > -1);
- if (length != Integer.MAX_VALUE && totalBytes < length) {
- throw new IOException("unexpected EOF");
+ if (maxLength != Integer.MAX_VALUE && totalBytes == maxLength) {
+ throw new IOException("MaxLength ("+maxLength+") reached - stream seems to be invalid.");
}
-
+
+ if (len != Integer.MAX_VALUE && totalBytes < len) {
+ throw new EOFException("unexpected EOF");
+ }
+
return baos.toByteArray();
}
@@ -350,19 +386,19 @@ public final class IOUtils {
*
* @param inp The {@link InputStream} which provides the data
* @param out The {@link OutputStream} to write the data to
+ * @return the amount of bytes copied
+ *
* @throws IOException If copying the data fails.
*/
- public static void copy(InputStream inp, OutputStream out) throws IOException {
- byte[] buff = new byte[4096];
- int count;
- while ((count = inp.read(buff)) != -1) {
- if (count < -1) {
- throw new RecordFormatException("Can't have read < -1 bytes");
- }
+ public static long copy(InputStream inp, OutputStream out) throws IOException {
+ final byte[] buff = new byte[4096];
+ long totalCount = 0;
+ for (int count; (count = inp.read(buff)) != -1; totalCount += count) {
if (count > 0) {
out.write(buff, 0, count);
}
}
+ return totalCount;
}
/**
@@ -370,16 +406,18 @@ public final class IOUtils {
*
* @param srcStream The {@link InputStream} which provides the data
* @param destFile The file where the data should be stored
+ * @return the amount of bytes copied
+ *
* @throws IOException If the target directory does not exist and cannot be created
* or if copying the data fails.
*/
- public static void copy(InputStream srcStream, File destFile) throws IOException {
+ public static long copy(InputStream srcStream, File destFile) throws IOException {
File destDirectory = destFile.getParentFile();
if (!(destDirectory.exists() || destDirectory.mkdirs())) {
throw new RuntimeException("Can't create destination directory: "+destDirectory);
}
try (OutputStream destStream = new FileOutputStream(destFile)) {
- IOUtils.copy(srcStream, destStream);
+ return IOUtils.copy(srcStream, destStream);
}
}