61346 add more sanity checks before allocating byte arrays in emf/wmf

git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1803041 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Tim Allison 2017-07-26 12:46:24 +00:00
parent bf7936bc42
commit a9c4072071
8 changed files with 53 additions and 23 deletions

View File

@ -431,4 +431,19 @@ public final class IOUtils {
return toSkip - remain; return toSkip - remain;
} }
public static byte[] safelyAllocate(long length, int maxLength) {
if (length < 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 (length > maxLength) {
throw new RecordFormatException("Not allowed to allocate an array > "+
maxLength+" for this record type." +
"If the file is not corrupt, please open an issue on bugzilla to request " +
"increasing the maximum allowable size for this record type");
}
return new byte[(int)length];
}
} }

View File

@ -23,6 +23,7 @@ import java.util.List;
import org.apache.poi.hemf.hemfplus.record.HemfPlusRecord; import org.apache.poi.hemf.hemfplus.record.HemfPlusRecord;
import org.apache.poi.hemf.hemfplus.record.HemfPlusRecordType; import org.apache.poi.hemf.hemfplus.record.HemfPlusRecordType;
import org.apache.poi.util.IOUtils;
import org.apache.poi.util.Internal; import org.apache.poi.util.Internal;
import org.apache.poi.util.LittleEndian; import org.apache.poi.util.LittleEndian;
import org.apache.poi.util.RecordFormatException; import org.apache.poi.util.RecordFormatException;
@ -33,6 +34,9 @@ import org.apache.poi.util.RecordFormatException;
@Internal @Internal
public class HemfCommentEMFPlus extends AbstractHemfComment { public class HemfCommentEMFPlus extends AbstractHemfComment {
private static final int MAX_RECORD_LENGTH = 1000000;
long dataSize; long dataSize;
public HemfCommentEMFPlus(byte[] rawBytes) { public HemfCommentEMFPlus(byte[] rawBytes) {
//these rawBytes contain only the EMFPlusRecord(s?) //these rawBytes contain only the EMFPlusRecord(s?)
@ -93,7 +97,7 @@ public class HemfCommentEMFPlus extends AbstractHemfComment {
} catch (IllegalAccessException e) { } catch (IllegalAccessException e) {
throw new RuntimeException(e); throw new RuntimeException(e);
} }
byte[] dataBytes = new byte[size]; byte[] dataBytes = IOUtils.safelyAllocate(size, MAX_RECORD_LENGTH);
System.arraycopy(bytes, offset, dataBytes, 0, size); System.arraycopy(bytes, offset, dataBytes, 0, size);
try { try {
record.init(dataBytes, recordId, flags); record.init(dataBytes, recordId, flags);

View File

@ -23,6 +23,7 @@ import java.io.InputStream;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import org.apache.poi.util.IOUtils;
import org.apache.poi.util.Internal; import org.apache.poi.util.Internal;
import org.apache.poi.util.LittleEndian; import org.apache.poi.util.LittleEndian;
import org.apache.poi.util.LittleEndianConsts; import org.apache.poi.util.LittleEndianConsts;
@ -35,6 +36,9 @@ import org.apache.poi.util.RecordFormatException;
@Internal @Internal
public class HemfCommentPublic { public class HemfCommentPublic {
private static final int MAX_RECORD_LENGTH = 1000000;
/** /**
* Stub, to be implemented * Stub, to be implemented
*/ */
@ -80,7 +84,7 @@ public class HemfCommentPublic {
} }
List<HemfMultiFormatsData> list = new ArrayList<HemfMultiFormatsData>(); List<HemfMultiFormatsData> list = new ArrayList<HemfMultiFormatsData>();
for (EmrFormat emrFormat : emrFormatList) { for (EmrFormat emrFormat : emrFormatList) {
byte[] data = new byte[emrFormat.size]; byte[] data = IOUtils.safelyAllocate(emrFormat.size, MAX_RECORD_LENGTH);
System.arraycopy(rawBytes, emrFormat.offset-4, data, 0, emrFormat.size); System.arraycopy(rawBytes, emrFormat.offset-4, data, 0, emrFormat.size);
list.add(new HemfMultiFormatsData(emrFormat.signature, emrFormat.version, data)); list.add(new HemfMultiFormatsData(emrFormat.signature, emrFormat.version, data));
} }
@ -129,12 +133,8 @@ public class HemfCommentPublic {
wmfBytes = new byte[0]; wmfBytes = new byte[0];
return; return;
} }
if (winMetafileSizeLong > Integer.MAX_VALUE) { wmfBytes = IOUtils.safelyAllocate(winMetafileSizeLong, MAX_RECORD_LENGTH);
throw new RecordFormatException("Metafile record length can't be > Integer.MAX_VALUE"); System.arraycopy(rawBytes, offset, wmfBytes, 0, wmfBytes.length);
}
int winMetafileSize = (int)winMetafileSizeLong;
wmfBytes = new byte[winMetafileSize];
System.arraycopy(rawBytes, offset, wmfBytes, 0, winMetafileSize);
} }
/** /**

View File

@ -36,6 +36,7 @@ import org.apache.poi.util.RecordFormatException;
*/ */
@Internal @Internal
public class HemfCommentRecord implements HemfRecord { public class HemfCommentRecord implements HemfRecord {
private static final int MAX_RECORD_LENGTH = 1000000;
public final static long COMMENT_EMFSPOOL = 0x00000000; public final static long COMMENT_EMFSPOOL = 0x00000000;
public final static long COMMENT_EMFPLUS = 0x2B464D45; public final static long COMMENT_EMFPLUS = 0x2B464D45;
@ -87,7 +88,7 @@ public class HemfCommentRecord implements HemfRecord {
int dataSize = (int)remainingDataSize; int dataSize = (int)remainingDataSize;
int recordSize = (int)remainingRecordSize; int recordSize = (int)remainingRecordSize;
byte[] arr = new byte[dataSize+initialBytes.length]; byte[] arr = IOUtils.safelyAllocate(dataSize+initialBytes.length, MAX_RECORD_LENGTH);
System.arraycopy(initialBytes,0,arr, 0, initialBytes.length); System.arraycopy(initialBytes,0,arr, 0, initialBytes.length);
long read = IOUtils.readFully(leis, arr, initialBytes.length, dataSize); long read = IOUtils.readFully(leis, arr, initialBytes.length, dataSize);
if (read != dataSize) { if (read != dataSize) {
@ -103,13 +104,12 @@ public class HemfCommentRecord implements HemfRecord {
} }
private byte[] readToByteArray(LittleEndianInputStream leis, long dataSize, long recordSize) throws IOException { private byte[] readToByteArray(LittleEndianInputStream leis, long dataSize, long recordSize) throws IOException {
assert dataSize < Integer.MAX_VALUE;
if (recordSize == 0) { if (recordSize == 0) {
return new byte[0]; return new byte[0];
} }
byte[] arr = new byte[(int)dataSize]; byte[] arr = IOUtils.safelyAllocate(dataSize, MAX_RECORD_LENGTH);
long read = IOUtils.readFully(leis, arr); long read = IOUtils.readFully(leis, arr);
if (read != dataSize) { if (read != dataSize) {

View File

@ -32,6 +32,9 @@ import org.apache.poi.util.LittleEndianInputStream;
@Internal @Internal
public class HemfHeader implements HemfRecord { public class HemfHeader implements HemfRecord {
private static final int MAX_RECORD_LENGTH = 1000000;
private Rectangle boundsRectangle; private Rectangle boundsRectangle;
private Rectangle frameRectangle; private Rectangle frameRectangle;
private long bytes; private long bytes;
@ -140,7 +143,7 @@ public class HemfHeader implements HemfRecord {
throw new IOException("Not a valid EMF header. Record type:"+recordId); throw new IOException("Not a valid EMF header. Record type:"+recordId);
} }
//read the record--id and size (2 bytes) have already been read //read the record--id and size (2 bytes) have already been read
byte[] data = new byte[(int)recordSize]; byte[] data = IOUtils.safelyAllocate(recordSize, MAX_RECORD_LENGTH);
IOUtils.readFully(leis, data); IOUtils.readFully(leis, data);
int offset = 0; int offset = 0;

View File

@ -39,7 +39,8 @@ import org.apache.poi.util.RecordFormatException;
@Internal @Internal
public class HemfText { public class HemfText {
private final static Charset UTF16LE = Charset.forName("UTF-16LE"); private static final Charset UTF16LE = Charset.forName("UTF-16LE");
private static final int MAX_RECORD_LENGTH = 1000000;
public static class ExtCreateFontIndirectW extends UnimplementedHemfRecord { public static class ExtCreateFontIndirectW extends UnimplementedHemfRecord {
} }
@ -80,7 +81,7 @@ public class HemfText {
} }
//guarantee to read the rest of the EMRTextObjectRecord //guarantee to read the rest of the EMRTextObjectRecord
//emrtextbytes start after 7*4 bytes read above //emrtextbytes start after 7*4 bytes read above
byte[] emrTextBytes = new byte[recordSizeInt-(7*LittleEndian.INT_SIZE)]; byte[] emrTextBytes = IOUtils.safelyAllocate(recordSizeInt-(7*LittleEndian.INT_SIZE), MAX_RECORD_LENGTH);
IOUtils.readFully(leis, emrTextBytes); IOUtils.readFully(leis, emrTextBytes);
textObject = new EmrTextObject(emrTextBytes, getEncodingHint(), 20);//should be 28, but recordSizeInt has already subtracted 8 textObject = new EmrTextObject(emrTextBytes, getEncodingHint(), 20);//should be 28, but recordSizeInt has already subtracted 8
return recordSize; return recordSize;
@ -218,9 +219,12 @@ public class HemfText {
} }
if (numCharsLong > Integer.MAX_VALUE) { if (numCharsLong > Integer.MAX_VALUE) {
throw new RecordFormatException("Number of characters can't be > Integer.MAX_VALUE"); throw new RecordFormatException("Number of characters can't be > Integer.MAX_VALUE");
} else if (numCharsLong < 0) {
throw new RecordFormatException("Number of characters can't be < 0");
} }
numChars = (int)numCharsLong; numChars = (int)numCharsLong;
rawTextBytes = new byte[emrTextObjBytes.length-start]; rawTextBytes = IOUtils.safelyAllocate(emrTextObjBytes.length-start, MAX_RECORD_LENGTH);
System.arraycopy(emrTextObjBytes, start, rawTextBytes, 0, emrTextObjBytes.length-start); System.arraycopy(emrTextObjBytes, start, rawTextBytes, 0, emrTextObjBytes.length-start);
} }

View File

@ -17,6 +17,7 @@
package org.apache.poi.hwmf.record; package org.apache.poi.hwmf.record;
import javax.imageio.ImageIO;
import java.awt.Color; import java.awt.Color;
import java.awt.Graphics2D; import java.awt.Graphics2D;
import java.awt.image.BufferedImage; import java.awt.image.BufferedImage;
@ -24,8 +25,6 @@ import java.io.ByteArrayInputStream;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import javax.imageio.ImageIO;
import org.apache.poi.util.IOUtils; import org.apache.poi.util.IOUtils;
import org.apache.poi.util.LittleEndian; import org.apache.poi.util.LittleEndian;
import org.apache.poi.util.LittleEndianConsts; import org.apache.poi.util.LittleEndianConsts;
@ -38,6 +37,9 @@ import org.apache.poi.util.RecordFormatException;
* The DeviceIndependentBitmap Object defines an image in device-independent bitmap (DIB) format. * The DeviceIndependentBitmap Object defines an image in device-independent bitmap (DIB) format.
*/ */
public class HwmfBitmapDib { public class HwmfBitmapDib {
private static final int MAX_RECORD_LENGTH = 10000000;
public static enum BitCount { public static enum BitCount {
/** /**
* The image SHOULD be in either JPEG or PNG format. <6> Neither of these formats includes * The image SHOULD be in either JPEG or PNG format. <6> Neither of these formats includes
@ -385,7 +387,7 @@ public class HwmfBitmapDib {
int imageSize = (int)Math.max(imageData.length, introSize+headerImageSize); int imageSize = (int)Math.max(imageData.length, introSize+headerImageSize);
// create the image data and leave the parsing to the ImageIO api // create the image data and leave the parsing to the ImageIO api
byte buf[] = new byte[BMP_HEADER_SIZE+imageSize]; byte buf[] = IOUtils.safelyAllocate(BMP_HEADER_SIZE+imageSize, MAX_RECORD_LENGTH);
// https://en.wikipedia.org/wiki/BMP_file_format # Bitmap file header // https://en.wikipedia.org/wiki/BMP_file_format # Bitmap file header
buf[0] = (byte)'B'; buf[0] = (byte)'B';

View File

@ -26,6 +26,7 @@ import org.apache.poi.hwmf.draw.HwmfGraphics;
import org.apache.poi.hwmf.record.HwmfMisc.WmfSetMapMode; import org.apache.poi.hwmf.record.HwmfMisc.WmfSetMapMode;
import org.apache.poi.util.BitField; import org.apache.poi.util.BitField;
import org.apache.poi.util.BitFieldFactory; import org.apache.poi.util.BitFieldFactory;
import org.apache.poi.util.IOUtils;
import org.apache.poi.util.LittleEndianConsts; import org.apache.poi.util.LittleEndianConsts;
import org.apache.poi.util.LittleEndianInputStream; import org.apache.poi.util.LittleEndianInputStream;
import org.apache.poi.util.POILogFactory; import org.apache.poi.util.POILogFactory;
@ -33,6 +34,7 @@ import org.apache.poi.util.POILogger;
public class HwmfText { public class HwmfText {
private static final POILogger logger = POILogFactory.getLogger(HwmfText.class); private static final POILogger logger = POILogFactory.getLogger(HwmfText.class);
private static final int MAX_RECORD_LENGTH = 1000000;
/** /**
* The META_SETTEXTCHAREXTRA record defines inter-character spacing for text justification in the * The META_SETTEXTCHAREXTRA record defines inter-character spacing for text justification in the
@ -164,7 +166,7 @@ public class HwmfText {
@Override @Override
public int init(LittleEndianInputStream leis, long recordSize, int recordFunction) throws IOException { public int init(LittleEndianInputStream leis, long recordSize, int recordFunction) throws IOException {
stringLength = leis.readShort(); stringLength = leis.readShort();
rawTextBytes = new byte[stringLength+(stringLength&1)]; rawTextBytes = IOUtils.safelyAllocate(stringLength+(stringLength&1), MAX_RECORD_LENGTH);
leis.readFully(rawTextBytes); leis.readFully(rawTextBytes);
yStart = leis.readShort(); yStart = leis.readShort();
xStart = leis.readShort(); xStart = leis.readShort();
@ -188,7 +190,7 @@ public class HwmfText {
* This does not include the extra optional padding on the byte array. * This does not include the extra optional padding on the byte array.
*/ */
private byte[] getTextBytes() { private byte[] getTextBytes() {
byte[] ret = new byte[stringLength]; byte[] ret = IOUtils.safelyAllocate(stringLength, MAX_RECORD_LENGTH);
System.arraycopy(rawTextBytes, 0, ret, 0, stringLength); System.arraycopy(rawTextBytes, 0, ret, 0, stringLength);
return ret; return ret;
} }
@ -315,7 +317,7 @@ public class HwmfText {
size += 4*LittleEndianConsts.SHORT_SIZE; size += 4*LittleEndianConsts.SHORT_SIZE;
} }
rawTextBytes = new byte[stringLength+(stringLength&1)]; rawTextBytes = IOUtils.safelyAllocate(stringLength+(stringLength&1), MAX_RECORD_LENGTH);
leis.readFully(rawTextBytes); leis.readFully(rawTextBytes);
size += rawTextBytes.length; size += rawTextBytes.length;
@ -355,7 +357,7 @@ public class HwmfText {
* This does not include the extra optional padding on the byte array. * This does not include the extra optional padding on the byte array.
*/ */
private byte[] getTextBytes() { private byte[] getTextBytes() {
byte[] ret = new byte[stringLength]; byte[] ret = IOUtils.safelyAllocate(stringLength, MAX_RECORD_LENGTH);
System.arraycopy(rawTextBytes, 0, ret, 0, stringLength); System.arraycopy(rawTextBytes, 0, ret, 0, stringLength);
return ret; return ret;
} }