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]

Reply via email to