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 d80ff2dbfb Validate EMF description bounds before allocation (#1060)
d80ff2dbfb is described below
commit d80ff2dbfba27d3b0ab303a0a4c49dabf4b4489f
Author: jmestwa-coder <[email protected]>
AuthorDate: Mon May 11 21:33:39 2026 +0530
Validate EMF description bounds before allocation (#1060)
* Validate EMF description bounds before allocation
* Potential fix for pull request finding
Co-authored-by: Copilot Autofix powered by AI
<[email protected]>
---------
Co-authored-by: Copilot Autofix powered by AI
<[email protected]>
---
.../org/apache/poi/hemf/record/emf/HemfHeader.java | 21 ++++--
.../apache/poi/hemf/usermodel/TestHemfPicture.java | 79 +++++++++++++++++++++-
2 files changed, 93 insertions(+), 7 deletions(-)
diff --git
a/poi-scratchpad/src/main/java/org/apache/poi/hemf/record/emf/HemfHeader.java
b/poi-scratchpad/src/main/java/org/apache/poi/hemf/record/emf/HemfHeader.java
index d8d3d34c7c..cb9ab2ebc5 100644
---
a/poi-scratchpad/src/main/java/org/apache/poi/hemf/record/emf/HemfHeader.java
+++
b/poi-scratchpad/src/main/java/org/apache/poi/hemf/record/emf/HemfHeader.java
@@ -32,9 +32,11 @@ import java.util.function.Supplier;
import org.apache.poi.util.Dimension2DDouble;
import org.apache.poi.util.GenericRecordJsonWriter;
+import org.apache.poi.util.IOUtils;
import org.apache.poi.util.Internal;
import org.apache.poi.util.LittleEndianConsts;
import org.apache.poi.util.LittleEndianInputStream;
+import org.apache.poi.util.RecordFormatException;
/**
* Extracts the full header from EMF files.
@@ -155,8 +157,8 @@ public class HemfHeader implements HemfRecord {
//reserved
leis.skipFully(LittleEndianConsts.SHORT_SIZE);
- int nDescription = (int)leis.readUInt();
- int offDescription = (int)leis.readUInt();
+ long nDescription = leis.readUInt();
+ long offDescription = leis.readUInt();
nPalEntries = leis.readUInt();
size += 8*LittleEndianConsts.INT_SIZE;
@@ -165,10 +167,17 @@ public class HemfHeader implements HemfRecord {
size += readDimensionInt(leis, milliDimension);
if (nDescription > 0 && offDescription > 0) {
- int skip = (int)(offDescription - (size + HEADER_SIZE));
- leis.mark(skip+nDescription*2);
- leis.skipFully(skip);
- byte[] buf = new byte[(nDescription-1)*2];
+ long skip = offDescription - (size + HEADER_SIZE);
+ long descriptionBytes = (nDescription - 1) *
LittleEndianConsts.SHORT_SIZE;
+ long descriptionEnd = offDescription + nDescription *
LittleEndianConsts.SHORT_SIZE;
+ if (skip < 0 || descriptionEnd > recordSize + HEADER_SIZE || skip
+ descriptionBytes > Integer.MAX_VALUE) {
+ throw new RecordFormatException("Invalid EMF header
description bounds");
+ }
+ int maxDescriptionLength = (int)Math.min(recordSize,
Integer.MAX_VALUE);
+ IOUtils.safelyAllocateCheck(descriptionBytes,
maxDescriptionLength);
+ leis.mark((int)(skip + descriptionBytes));
+ leis.skipFully((int)skip);
+ byte[] buf = IOUtils.safelyAllocate(descriptionBytes,
maxDescriptionLength);
leis.readFully(buf);
description = new String(buf,
StandardCharsets.UTF_16LE).replace((char)0, ' ').trim();
leis.reset();
diff --git
a/poi-scratchpad/src/test/java/org/apache/poi/hemf/usermodel/TestHemfPicture.java
b/poi-scratchpad/src/test/java/org/apache/poi/hemf/usermodel/TestHemfPicture.java
index 22740b0cf6..7fa968d9d3 100644
---
a/poi-scratchpad/src/test/java/org/apache/poi/hemf/usermodel/TestHemfPicture.java
+++
b/poi-scratchpad/src/test/java/org/apache/poi/hemf/usermodel/TestHemfPicture.java
@@ -25,6 +25,7 @@ import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
import java.awt.geom.Point2D;
+import java.io.ByteArrayOutputStream;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
@@ -49,6 +50,8 @@ import org.apache.poi.hwmf.usermodel.HwmfEmbedded;
import org.apache.poi.hwmf.usermodel.HwmfEmbeddedType;
import org.apache.poi.hwmf.usermodel.HwmfPicture;
import org.apache.poi.util.IOUtils;
+import org.apache.poi.util.LittleEndianInputStream;
+import org.apache.poi.util.LittleEndianOutputStream;
import org.apache.poi.util.RecordFormatException;
import org.junit.jupiter.api.Test;
@@ -229,6 +232,28 @@ public class TestHemfPicture {
}
}
+ @Test
+ void testHeaderDescriptionBoundsAreValidated() throws Exception {
+ HemfHeader header = new HemfHeader();
+ byte[] data = createHeaderRecordData(0x40000001L, 88);
+
+ try (LittleEndianInputStream leis = new LittleEndianInputStream(new
ByteArrayInputStream(data))) {
+ assertThrows(RecordFormatException.class,
+ () -> header.init(leis, data.length,
HemfRecordType.header.id));
+ }
+ }
+
+ @Test
+ void testHeaderDescriptionParsesValidUtf16lePayload() throws Exception {
+ HemfHeader header = new HemfHeader();
+ byte[] data = createHeaderRecordDataWithDescription("POI");
+
+ try (LittleEndianInputStream leis = new LittleEndianInputStream(new
ByteArrayInputStream(data))) {
+ header.init(leis, data.length, HemfRecordType.header.id);
+ assertEquals("POI", header.getDescription());
+ }
+ }
+
@Test
void nestedWmfEmf() throws Exception {
try (InputStream is =
sl_samples.openResourceAsStream("nested_wmf.emf")) {
@@ -253,4 +278,56 @@ public class TestHemfPicture {
/* govdocs1 064213.doc-0.emf contains an example of extextouta */
-}
\ No newline at end of file
+
+ private static byte[] createHeaderRecordData(long nDescription, long
offDescription) {
+ ByteArrayOutputStream bos = new ByteArrayOutputStream();
+ LittleEndianOutputStream leos = new LittleEndianOutputStream(bos);
+
+ for (int i = 0; i < 8; i++) {
+ leos.writeInt(0);
+ }
+ leos.writeInt(0x464D4520);
+ leos.writeInt(0x00010000);
+ leos.writeUInt(0);
+ leos.writeUInt(1);
+ leos.writeShort(1);
+ leos.writeShort(0);
+ leos.writeUInt(nDescription);
+ leos.writeUInt(offDescription);
+ leos.writeUInt(0);
+ for (int i = 0; i < 4; i++) {
+ leos.writeInt(0);
+ }
+
+ return bos.toByteArray();
+ }
+
+ private static byte[] createHeaderRecordDataWithDescription(String
description) {
+ ByteArrayOutputStream bos = new ByteArrayOutputStream();
+ LittleEndianOutputStream leos = new LittleEndianOutputStream(bos);
+
+ byte[] descriptionBytes =
description.getBytes(StandardCharsets.UTF_16LE);
+ long nDescription = description.length() + 1L;
+ long offDescription = 88L;
+
+ for (int i = 0; i < 8; i++) {
+ leos.writeInt(0);
+ }
+ leos.writeInt(0x464D4520);
+ leos.writeInt(0x00010000);
+ leos.writeUInt(0);
+ leos.writeUInt(1);
+ leos.writeShort(1);
+ leos.writeShort(0);
+ leos.writeUInt(nDescription);
+ leos.writeUInt(offDescription);
+ leos.writeUInt(0);
+ for (int i = 0; i < 4; i++) {
+ leos.writeInt(0);
+ }
+
+ leos.write(descriptionBytes);
+ leos.writeShort(0);
+ return bos.toByteArray();
+ }
+}
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]