This is an automated email from the ASF dual-hosted git repository.
pjfanning pushed a commit to branch trunk
in repository https://gitbox.apache.org/repos/asf/poi.git
The following commit(s) were added to refs/heads/trunk by this push:
new b7f1f55a47 hemf specific limits (#1114)
b7f1f55a47 is described below
commit b7f1f55a4745c71bab98736fea74ce189a7b3cf5
Author: PJ Fanning <[email protected]>
AuthorDate: Thu Jun 4 13:12:39 2026 +0100
hemf specific limits (#1114)
* hemf specific limits
* Update HemfComment.java
* refactor
---
.../apache/poi/hemf/record/emf/HemfComment.java | 35 ++++++++++++++++++----
.../org/apache/poi/hemf/record/emf/HemfDraw.java | 24 ++++++++++++---
.../org/apache/poi/hemf/record/emf/HemfText.java | 3 +-
.../poi/hemf/record/emfplus/HemfPlusBrush.java | 9 +++---
.../poi/hemf/record/emfplus/HemfPlusPath.java | 4 +--
.../poi/hemf/usermodel/HemfEmbeddedIterator.java | 3 +-
.../org/apache/poi/hemf/usermodel/HemfPicture.java | 26 ++++++++++++++++
.../main/java/org/apache/poi/util/ArrayUtil.java | 12 ++++++++
poi/src/main/java/org/apache/poi/util/IOUtils.java | 35 ++++++++++++++++++++--
9 files changed, 130 insertions(+), 21 deletions(-)
diff --git
a/poi-scratchpad/src/main/java/org/apache/poi/hemf/record/emf/HemfComment.java
b/poi-scratchpad/src/main/java/org/apache/poi/hemf/record/emf/HemfComment.java
index 2aeaa3716c..29e49bb41f 100644
---
a/poi-scratchpad/src/main/java/org/apache/poi/hemf/record/emf/HemfComment.java
+++
b/poi-scratchpad/src/main/java/org/apache/poi/hemf/record/emf/HemfComment.java
@@ -40,7 +40,7 @@ import org.apache.poi.hemf.record.emf.HemfRecord.RenderBounds;
import org.apache.poi.hemf.record.emfplus.HemfPlusRecord;
import org.apache.poi.hemf.record.emfplus.HemfPlusRecordIterator;
import org.apache.poi.hwmf.usermodel.HwmfCharsetAware;
-import org.apache.poi.hwmf.usermodel.HwmfPicture;
+import org.apache.poi.util.ArrayUtil;
import org.apache.poi.util.GenericRecordJsonWriter;
import org.apache.poi.util.GenericRecordUtil;
import org.apache.poi.util.IOUtils;
@@ -56,6 +56,27 @@ import org.apache.poi.util.RecordFormatException;
@Internal
public class HemfComment {
private static final Logger LOG =
PoiLogManager.getLogger(HemfComment.class);
+ /** Max. record length - processing longer records will throw an exception
*/
+ private static final int DEFAULT_MAX_RECORD_LENGTH = 1_000_000;
+ private static int MAX_RECORD_LENGTH = DEFAULT_MAX_RECORD_LENGTH;
+
+ /**
+ * @param length the max record length allowed for HemfComment
+ */
+ public static void setMaxRecordLength(int length) {
+ MAX_RECORD_LENGTH = length;
+ }
+
+ /**
+ * @return the max record length allowed for HemfComment
+ */
+ public static int getMaxRecordLength() {
+ return MAX_RECORD_LENGTH;
+ }
+
+ public static void safelyAllocateCheck(long length) {
+ ArrayUtil.safelyAllocateCheck(length, getMaxRecordLength(),
"HemfComment.setMaxRecordLength()");
+ }
public enum HemfCommentRecordType {
emfGeneric(-1, EmfCommentDataGeneric::new, false),
@@ -286,7 +307,8 @@ public class HemfComment {
@Override
public long init(LittleEndianInputStream leis, long dataSize) throws
IOException {
- privateData = IOUtils.safelyAllocate(dataSize,
HwmfPicture.getMaxRecordLength());
+ privateData = IOUtils.safelyAllocate(dataSize,
getMaxRecordLength(),
+ "HemfComment.setMaxRecordLength()");
leis.readFully(privateData);
return privateData.length;
}
@@ -388,7 +410,8 @@ public class HemfComment {
// The number of Unicode characters in the optional description
string that follows.
int nDescription = Math.toIntExact(leis.readUInt());
- byte[] buf = IOUtils.safelyAllocate(nDescription * 2L,
HwmfPicture.getMaxRecordLength());
+ byte[] buf = IOUtils.safelyAllocate(nDescription * 2L,
getMaxRecordLength(),
+ "HemfComment.setMaxRecordLength()");
leis.readFully(buf);
description = new String(buf, StandardCharsets.UTF_16LE);
@@ -463,7 +486,8 @@ public class HemfComment {
for (EmfCommentDataFormat fmt : formats) {
int skip = fmt.offData-(leis.getReadIndex()-startIdx);
leis.skipFully(skip);
- fmt.rawData = IOUtils.safelyAllocate(fmt.sizeData,
HwmfPicture.getMaxRecordLength());
+ fmt.rawData = IOUtils.safelyAllocate(fmt.sizeData,
getMaxRecordLength(),
+ "HemfComment.setMaxRecordLength()");
int readBytes = leis.read(fmt.rawData);
if (readBytes < fmt.sizeData) {
// EOF
@@ -605,7 +629,8 @@ public class HemfComment {
// WMF metafile in the WinMetafile field.
int winMetafileSize = Math.toIntExact(leis.readUInt());
- wmfData = IOUtils.safelyAllocate(winMetafileSize,
HwmfPicture.getMaxRecordLength());
+ wmfData = IOUtils.safelyAllocate(winMetafileSize,
getMaxRecordLength(),
+ "HemfComment.setMaxRecordLength()");
// some emf comments are truncated, so we don't use readFully here
int readBytes = leis.read(wmfData);
if (readBytes < wmfData.length) {
diff --git
a/poi-scratchpad/src/main/java/org/apache/poi/hemf/record/emf/HemfDraw.java
b/poi-scratchpad/src/main/java/org/apache/poi/hemf/record/emf/HemfDraw.java
index dca405cc90..440f8931a2 100644
--- a/poi-scratchpad/src/main/java/org/apache/poi/hemf/record/emf/HemfDraw.java
+++ b/poi-scratchpad/src/main/java/org/apache/poi/hemf/record/emf/HemfDraw.java
@@ -34,10 +34,11 @@ import java.util.stream.IntStream;
import org.apache.poi.hemf.draw.HemfDrawProperties;
import org.apache.poi.hemf.draw.HemfGraphics;
+import org.apache.poi.hemf.usermodel.HemfPicture;
import org.apache.poi.hwmf.draw.HwmfGraphics.FillDrawStyle;
import org.apache.poi.hwmf.record.HwmfDraw;
import org.apache.poi.hwmf.record.HwmfDraw.WmfSelectObject;
-import org.apache.poi.hwmf.usermodel.HwmfPicture;
+import org.apache.poi.util.ArrayUtil;
import org.apache.poi.util.GenericRecordJsonWriter;
import org.apache.poi.util.GenericRecordUtil;
import org.apache.poi.util.IOUtils;
@@ -46,7 +47,22 @@ import org.apache.poi.util.LittleEndianInputStream;
public final class HemfDraw {
// arbitrary limit to avoid OOM on malformed files. This may need
increasing if "normal" files have more than this
- public static final int MAX_NUMBER_OF_POLYGONS = 100_000;
+ private static final int DEFAULT_MAX_NUMBER_OF_POLYGONS = 100_000;
+ private static int MAX_NUMBER_OF_POLYGONS = DEFAULT_MAX_NUMBER_OF_POLYGONS;
+
+ /**
+ * @since 2.0.0
+ */
+ public static void setMaxNumberOfPolygons(int length) {
+ MAX_NUMBER_OF_POLYGONS = length;
+ }
+
+ /**
+ * @since 2.0.0
+ */
+ public static int getMaxNumberOfPolygons() {
+ return MAX_NUMBER_OF_POLYGONS;
+ }
private HemfDraw() {}
@@ -506,7 +522,7 @@ public final class HemfDraw {
size += 2 * LittleEndianConsts.INT_SIZE;
// An array of 32-bit unsigned integers that specifies the point
count for each polygon.
- IOUtils.safelyAllocateCheck(numberOfPolygons,
MAX_NUMBER_OF_POLYGONS);
+ ArrayUtil.safelyAllocateCheck(numberOfPolygons,
getMaxNumberOfPolygons(), "HemfDraw.setMaxNumberOfPolygons()");
long[] polygonPointCount = new
long[Math.toIntExact(numberOfPolygons)];
size += numberOfPolygons * LittleEndianConsts.INT_SIZE;
@@ -948,7 +964,7 @@ public final class HemfDraw {
long size = readRectL(leis, bounds);
int count = Math.toIntExact(leis.readUInt());
size += LittleEndianConsts.INT_SIZE;
- IOUtils.safelyAllocateCheck(count,
HwmfPicture.getMaxRecordLength());
+ HemfPicture.safelyAllocateCheck(count);
Point2D[] points = new Point2D[count];
for (int i=0; i<count; i++) {
points[i] = new Point2D.Double();
diff --git
a/poi-scratchpad/src/main/java/org/apache/poi/hemf/record/emf/HemfText.java
b/poi-scratchpad/src/main/java/org/apache/poi/hemf/record/emf/HemfText.java
index efe73fdf90..96f42549b9 100644
--- a/poi-scratchpad/src/main/java/org/apache/poi/hemf/record/emf/HemfText.java
+++ b/poi-scratchpad/src/main/java/org/apache/poi/hemf/record/emf/HemfText.java
@@ -180,7 +180,8 @@ public class HemfText {
// read all available bytes and not just
"stringLength * 1(ansi)/2(unicode)"
// in case we need to deal with surrogate pairs
final int maxSize =
Math.toIntExact(Math.min(recordSize, strEnd)-size);
- rawTextBytes = IOUtils.safelyAllocate(maxSize,
MAX_RECORD_LENGTH);
+ rawTextBytes = IOUtils.safelyAllocate(maxSize,
MAX_RECORD_LENGTH,
+ "HemfText.setMaxRecordLength()");
leis.readFully(rawTextBytes);
size += maxSize;
break;
diff --git
a/poi-scratchpad/src/main/java/org/apache/poi/hemf/record/emfplus/HemfPlusBrush.java
b/poi-scratchpad/src/main/java/org/apache/poi/hemf/record/emfplus/HemfPlusBrush.java
index 7237ec4443..c819092038 100644
---
a/poi-scratchpad/src/main/java/org/apache/poi/hemf/record/emfplus/HemfPlusBrush.java
+++
b/poi-scratchpad/src/main/java/org/apache/poi/hemf/record/emfplus/HemfPlusBrush.java
@@ -27,7 +27,6 @@ import java.awt.geom.AffineTransform;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.io.IOException;
-import java.io.InputStream;
import java.util.AbstractMap;
import java.util.Arrays;
import java.util.Collections;
@@ -51,9 +50,9 @@ import
org.apache.poi.hemf.record.emfplus.HemfPlusImage.EmfPlusWrapMode;
import org.apache.poi.hemf.record.emfplus.HemfPlusObject.EmfPlusObjectData;
import org.apache.poi.hemf.record.emfplus.HemfPlusObject.EmfPlusObjectType;
import org.apache.poi.hemf.record.emfplus.HemfPlusPath.EmfPlusPath;
+import org.apache.poi.hemf.usermodel.HemfPicture;
import org.apache.poi.hwmf.record.HwmfBrushStyle;
import org.apache.poi.hwmf.record.HwmfColorRef;
-import org.apache.poi.hwmf.usermodel.HwmfPicture;
import org.apache.poi.sl.draw.DrawPaint;
import org.apache.poi.util.BitField;
import org.apache.poi.util.BitFieldFactory;
@@ -736,7 +735,7 @@ public class HemfPlusBrush {
// An array of SurroundingColorCount EmfPlusARGB objects that
specify the colors for discrete points on the
// boundary of the brush.
- IOUtils.safelyAllocateCheck(colorCount,
HwmfPicture.getMaxRecordLength());
+ HemfPicture.safelyAllocateCheck(colorCount);
surroundingColor = new Color[colorCount];
for (int i = 0; i < colorCount; i++) {
surroundingColor[i] = readARGB(leis.readInt());
@@ -759,7 +758,7 @@ public class HemfPlusBrush {
size += LittleEndianConsts.INT_SIZE;
// An array of BoundaryPointCount EmfPlusPointF objects that
specify the boundary of the brush.
- IOUtils.safelyAllocateCheck(pointCount,
HwmfPicture.getMaxRecordLength());
+ HemfPicture.safelyAllocateCheck(pointCount);
boundaryPoints = new Point2D[pointCount];
for (int i=0; i<pointCount; i++) {
size += readPointF(leis, boundaryPoints[i] = new
Point2D.Double());
@@ -915,7 +914,7 @@ public class HemfPlusBrush {
final int count = leis.readInt();
int size = LittleEndianConsts.INT_SIZE;
- IOUtils.safelyAllocateCheck(count, HwmfPicture.getMaxRecordLength());
+ HemfPicture.safelyAllocateCheck(count);
float[] positions = new float[count];
for (int i=0; i<count; i++) {
positions[i] = leis.readFloat();
diff --git
a/poi-scratchpad/src/main/java/org/apache/poi/hemf/record/emfplus/HemfPlusPath.java
b/poi-scratchpad/src/main/java/org/apache/poi/hemf/record/emfplus/HemfPlusPath.java
index cef5968044..29a17d1dd3 100644
---
a/poi-scratchpad/src/main/java/org/apache/poi/hemf/record/emfplus/HemfPlusPath.java
+++
b/poi-scratchpad/src/main/java/org/apache/poi/hemf/record/emfplus/HemfPlusPath.java
@@ -38,7 +38,7 @@ import
org.apache.poi.hemf.record.emfplus.HemfPlusDraw.EmfPlusRelativePosition;
import
org.apache.poi.hemf.record.emfplus.HemfPlusHeader.EmfPlusGraphicsVersion;
import org.apache.poi.hemf.record.emfplus.HemfPlusObject.EmfPlusObjectData;
import org.apache.poi.hemf.record.emfplus.HemfPlusObject.EmfPlusObjectType;
-import org.apache.poi.hwmf.usermodel.HwmfPicture;
+import org.apache.poi.hemf.usermodel.HemfPicture;
import org.apache.poi.util.BitField;
import org.apache.poi.util.BitFieldFactory;
import org.apache.poi.util.GenericRecordUtil;
@@ -121,7 +121,7 @@ public class HemfPlusPath {
readPoint = HemfPlusDraw::readPointF;
}
- IOUtils.safelyAllocateCheck(pointCount,
HwmfPicture.getMaxRecordLength());
+ HemfPicture.safelyAllocateCheck(pointCount);
pathPoints = new Point2D[pointCount];
for (int i=0; i<pointCount; i++) {
pathPoints[i] = new Point2D.Double();
diff --git
a/poi-scratchpad/src/main/java/org/apache/poi/hemf/usermodel/HemfEmbeddedIterator.java
b/poi-scratchpad/src/main/java/org/apache/poi/hemf/usermodel/HemfEmbeddedIterator.java
index c0c2763ac8..89f8799614 100644
---
a/poi-scratchpad/src/main/java/org/apache/poi/hemf/usermodel/HemfEmbeddedIterator.java
+++
b/poi-scratchpad/src/main/java/org/apache/poi/hemf/usermodel/HemfEmbeddedIterator.java
@@ -316,7 +316,8 @@ public class HemfEmbeddedIterator implements
Iterator<HwmfEmbedded> {
final int totalSize = epo.getTotalObjectSize() == 0
? ((EmfPlusImage)epo.getObjectData()).getImageData().length
: epo.getTotalObjectSize();
- IOUtils.safelyAllocateCheck(totalSize, MAX_RECORD_LENGTH);
+ IOUtils.safelyAllocateCheck(totalSize, MAX_RECORD_LENGTH,
+ "HemfEmbeddedIterator.setMaxRecordLength()");
try (UnsynchronizedByteArrayOutputStream bos =
UnsynchronizedByteArrayOutputStream.builder().setBufferSize(totalSize).get()) {
boolean hasNext = false;
diff --git
a/poi-scratchpad/src/main/java/org/apache/poi/hemf/usermodel/HemfPicture.java
b/poi-scratchpad/src/main/java/org/apache/poi/hemf/usermodel/HemfPicture.java
index 95d73082b8..7f1a5e37d1 100644
---
a/poi-scratchpad/src/main/java/org/apache/poi/hemf/usermodel/HemfPicture.java
+++
b/poi-scratchpad/src/main/java/org/apache/poi/hemf/usermodel/HemfPicture.java
@@ -52,7 +52,9 @@ import org.apache.poi.hemf.record.emf.HemfRecordIterator;
import org.apache.poi.hwmf.usermodel.HwmfCharsetAware;
import org.apache.poi.hwmf.usermodel.HwmfEmbedded;
import org.apache.poi.sl.draw.Drawable;
+import org.apache.poi.util.ArrayUtil;
import org.apache.poi.util.Dimension2DDouble;
+import org.apache.poi.util.IOUtils;
import org.apache.poi.util.Internal;
import org.apache.poi.util.LittleEndianInputStream;
import org.apache.poi.util.LocaleUtil;
@@ -64,11 +66,35 @@ import org.apache.poi.util.Units;
*/
@Internal
public class HemfPicture implements Iterable<HemfRecord>, GenericRecord {
+ /** Max. record length - processing longer records will throw an exception
*/
+ private static final int DEFAULT_MAX_RECORD_LENGTH = 1_000_000;
+ private static int MAX_RECORD_LENGTH = DEFAULT_MAX_RECORD_LENGTH;
+
private final LittleEndianInputStream stream;
private final List<HemfRecord> records = new ArrayList<>();
private boolean isParsed = false;
private Charset defaultCharset = LocaleUtil.CHARSET_1252;
+ /**
+ * @param length the max record length allowed for HemfPicture
+ * @since 2.0.0
+ */
+ public static void setMaxRecordLength(int length) {
+ MAX_RECORD_LENGTH = length;
+ }
+
+ /**
+ * @return the max record length allowed for HemfPicture
+ * @since 2.0.0
+ */
+ public static int getMaxRecordLength() {
+ return MAX_RECORD_LENGTH;
+ }
+
+ public static void safelyAllocateCheck(long length) {
+ ArrayUtil.safelyAllocateCheck(length, getMaxRecordLength(),
"HemfPicture.setMaxRecordLength()");
+ }
+
public HemfPicture(InputStream is) {
this(new LittleEndianInputStream(is));
}
diff --git a/poi/src/main/java/org/apache/poi/util/ArrayUtil.java
b/poi/src/main/java/org/apache/poi/util/ArrayUtil.java
index 9878be93ca..54f88bd492 100644
--- a/poi/src/main/java/org/apache/poi/util/ArrayUtil.java
+++ b/poi/src/main/java/org/apache/poi/util/ArrayUtil.java
@@ -83,4 +83,16 @@ public final class ArrayUtil {
// We're done - array will now have everything moved as required
}
+ public static void safelyAllocateCheck(long length, int maxLength, String
limitMethod) {
+ if (length < 0L) {
+ throw new RecordFormatException("Can't allocate an array of length
< 0, but had " + length + " and " + maxLength);
+ }
+ if (length > (long)Integer.MAX_VALUE) {
+ throw new RecordFormatException("Can't allocate an array > " +
Integer.MAX_VALUE);
+ }
+ if (length > maxLength) {
+ IOUtils.throwRFE(length, maxLength, limitMethod);
+ }
+ }
+
}
diff --git a/poi/src/main/java/org/apache/poi/util/IOUtils.java
b/poi/src/main/java/org/apache/poi/util/IOUtils.java
index 2debe8e419..01e758fdbf 100644
--- a/poi/src/main/java/org/apache/poi/util/IOUtils.java
+++ b/poi/src/main/java/org/apache/poi/util/IOUtils.java
@@ -310,6 +310,15 @@ public final class IOUtils {
}
}
+ private static void checkLength(long length, int maxLength, String
limitMethod) {
+ 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, limitMethod);
+ }
+ }
/**
* Returns an array (that shouldn't be written to!) of the
@@ -580,9 +589,15 @@ public final class IOUtils {
public static byte[] safelyAllocate(long length, int maxLength) {
safelyAllocateCheck(length, maxLength);
+ try {
+ return new byte[Math.toIntExact(length)];
+ } catch (ArithmeticException e) {
+ throw new RecordFormatException("Int Overflow with length", e);
+ }
+ }
- checkByteSizeLimit(length);
-
+ public static byte[] safelyAllocate(long length, int maxLength, String
limitMethod) {
+ safelyAllocateCheck(length, maxLength);
try {
return new byte[Math.toIntExact(length)];
} catch (ArithmeticException e) {
@@ -600,6 +615,16 @@ public final class IOUtils {
checkLength(length, maxLength);
}
+ public static void safelyAllocateCheck(long length, int maxLength, String
limitMethod) {
+ if (length < 0L) {
+ throw new RecordFormatException("Can't allocate an array of length
< 0, but had " + length + " and " + maxLength);
+ }
+ if (length > (long)Integer.MAX_VALUE) {
+ throw new RecordFormatException("Can't allocate an array > " +
Integer.MAX_VALUE);
+ }
+ checkLength(length, maxLength, limitMethod);
+ }
+
public static byte[] safelyClone(byte[] src, int offset, int length, int
maxLength) {
if (src == null) {
return null;
@@ -654,11 +679,15 @@ public final class IOUtils {
}
private static void throwRFE(long length, int maxLength) {
+ throwRFE(length, maxLength, "IOUtils.setByteArrayMaxOverride()");
+ }
+
+ static void throwRFE(long length, int maxLength, String limitMethod) {
throw new RecordFormatException(String.format(Locale.ROOT, "Tried to
allocate an array of length %,d" +
", but the maximum length for this record type is %,d.%n" +
"If the file is not corrupt and not large, please open an
issue on bugzilla to request %n" +
"increasing the maximum allowable size for this record
type.%n" +
- "You can set a higher override value with
IOUtils.setByteArrayMaxOverride()", length, maxLength));
+ "You can set a higher override value with %s", length,
maxLength, limitMethod));
}
private static void throwRecordTruncationException(final int maxLength) {
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]