diff options
author | Andreas Beeker <kiwiwings@apache.org> | 2018-04-27 21:38:19 +0000 |
---|---|---|
committer | Andreas Beeker <kiwiwings@apache.org> | 2018-04-27 21:38:19 +0000 |
commit | f94245e9d876c49462bc66bdc573ea11160b617a (patch) | |
tree | b3ad7edff8643e1fdb024a568f1e071f98232537 /src/java | |
parent | 48f03cd45abcef0ca26e91e2080f430557a2c70b (diff) | |
download | poi-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')
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 >0 but <N bytes, + * or a PushbackInputStream. If the stream has >0 but <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); } } |