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 a7d9bbfcd7 HSLF: reject oversized PersistPtr sheet offsets (#1078)
a7d9bbfcd7 is described below
commit a7d9bbfcd726c2423704c4305b91b66a507ef947
Author: metsw24-max <[email protected]>
AuthorDate: Tue May 26 02:58:58 2026 +0530
HSLF: reject oversized PersistPtr sheet offsets (#1078)
HSLF: reject oversized PersistPtr sheet offsets
---
.../apache/poi/hslf/record/PersistPtrHolder.java | 9 ++-
.../poi/hslf/record/TestPersistPtrHolder.java | 82 ++++++++++++++++++++++
2 files changed, 90 insertions(+), 1 deletion(-)
diff --git
a/poi-scratchpad/src/main/java/org/apache/poi/hslf/record/PersistPtrHolder.java
b/poi-scratchpad/src/main/java/org/apache/poi/hslf/record/PersistPtrHolder.java
index db3482f0aa..29697716cf 100644
---
a/poi-scratchpad/src/main/java/org/apache/poi/hslf/record/PersistPtrHolder.java
+++
b/poi-scratchpad/src/main/java/org/apache/poi/hslf/record/PersistPtrHolder.java
@@ -127,7 +127,14 @@ public final class PersistPtrHolder extends
PositionDependentRecordAtom {
// Grab the offsets for each of the sheets
for(int i=0; i<offset_count; i++) {
int sheet_no = offset_no + i;
- int sheet_offset = (int)LittleEndian.getUInt(_ptrData,pos);
+ // Reject sheet offsets that would silently narrow when the
+ // uint32 read here is cast to signed int. _slideLocations
+ // values feed Record.buildRecordAtOffset(docstream, offset),
+ // where a wrapped negative offset can land on valid byte
+ // positions of the docstream and cause the parser to read
+ // a record from the wrong location instead of failing
+ // cleanly on the malformed input.
+ int sheet_offset =
Math.toIntExact(LittleEndian.getUInt(_ptrData,pos));
_slideLocations.put(sheet_no, sheet_offset);
// Wind on by 4 bytes per sheet found
diff --git
a/poi-scratchpad/src/test/java/org/apache/poi/hslf/record/TestPersistPtrHolder.java
b/poi-scratchpad/src/test/java/org/apache/poi/hslf/record/TestPersistPtrHolder.java
new file mode 100644
index 0000000000..7db86a6aa5
--- /dev/null
+++
b/poi-scratchpad/src/test/java/org/apache/poi/hslf/record/TestPersistPtrHolder.java
@@ -0,0 +1,82 @@
+/* ====================================================================
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+==================================================================== */
+
+package org.apache.poi.hslf.record;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+import org.apache.poi.util.LittleEndian;
+import org.junit.jupiter.api.Test;
+
+public final class TestPersistPtrHolder {
+
+ /**
+ * Build the byte representation of a PersistPtrIncrementalBlock with a
+ * single PersistDirectoryEntry pointing to {@code sheetOffset}. The
+ * outer 8-byte record header is included so the buffer can be passed
+ * straight to {@link PersistPtrHolder#PersistPtrHolder(byte[], int, int)}.
+ */
+ private static byte[] buildRecord(long sheetOffset) {
+ byte[] buf = new byte[16];
+ // Record header (8 bytes): version/instance, type, length
+ LittleEndian.putUShort(buf, 0, 0); //
version/instance (unused here)
+ LittleEndian.putUShort(buf, 2, (int)
RecordTypes.PersistPtrIncrementalBlock.typeID);
+ LittleEndian.putInt(buf, 4, 8); // body length =
info(4) + offset(4)
+
+ // Body: info field with cntPersist=1, persistId=0, then the 32-bit
offset
+ LittleEndian.putInt(buf, 8, 0x00100000); // cntPersistFld
bits = 1, persistIdFld bits = 0
+ LittleEndian.putUInt(buf, 12, sheetOffset);
+ return buf;
+ }
+
+ /**
+ * A crafted PersistPtrHolder entry whose sheet offset is >
+ * {@link Integer#MAX_VALUE} used to be silently narrowed via a plain
+ * {@code (int)} cast at parse time. The wrapped negative value was then
+ * stored in {@code _slideLocations} and later flowed into
+ * {@code Record.buildRecordAtOffset(docstream, offset)} - where a small
+ * negative offset can land on valid byte positions of the docstream
+ * (since {@code b[offset+2]} can be a valid index when {@code offset} is
+ * -1 or -2) and cause the parser to read a record from the wrong
+ * location instead of failing on the malformed input. Reject these at
+ * parse time via {@link Math#toIntExact(long)}, matching the recent
+ * hardening of HDGF v6+ chunk Length and the PointerFactory uint32
+ * fields.
+ */
+ @Test
+ void testRejectsOversizedSheetOffset() {
+ byte[] data = buildRecord(0x80000001L);
+ assertThrows(ArithmeticException.class,
+ () -> new PersistPtrHolder(data, 0, data.length));
+ }
+
+ /**
+ * Equally important: the hardening must not introduce a new lower
+ * ceiling. Offsets up to {@link Integer#MAX_VALUE} are still
+ * representable as a signed int and must continue to parse - byte
+ * arrays in the JVM can be that large in principle, and downstream
+ * code already bounds-checks against the actual docstream length.
+ */
+ @Test
+ void testAcceptsMaxIntSheetOffset() {
+ byte[] data = buildRecord(Integer.MAX_VALUE);
+ PersistPtrHolder ptr = new PersistPtrHolder(data, 0, data.length);
+ Integer offset = ptr.getSlideLocationsLookup().get(0);
+ assertEquals(Integer.MAX_VALUE, offset.intValue());
+ }
+}
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]