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 2d79abc001 guard XSSFBUtils wide-string byte length against int
overflow (#1079)
2d79abc001 is described below
commit 2d79abc0017c96d29c6367a8c255876fc584e535
Author: jmestwa-coder <[email protected]>
AuthorDate: Wed May 27 18:50:21 2026 +0530
guard XSSFBUtils wide-string byte length against int overflow (#1079)
* guard XSSFBUtils wide-string byte length against int overflow
* use checked narrowing for numBytes in XSSFBUtils string append
---
.../org/apache/poi/xssf/binary/XSSFBUtils.java | 14 ++---
.../org/apache/poi/xssf/binary/TestXSSFBUtils.java | 72 ++++++++++++++++++++++
2 files changed, 78 insertions(+), 8 deletions(-)
diff --git a/poi-ooxml/src/main/java/org/apache/poi/xssf/binary/XSSFBUtils.java
b/poi-ooxml/src/main/java/org/apache/poi/xssf/binary/XSSFBUtils.java
index cef9e6807f..a17a989fc7 100644
--- a/poi-ooxml/src/main/java/org/apache/poi/xssf/binary/XSSFBUtils.java
+++ b/poi-ooxml/src/main/java/org/apache/poi/xssf/binary/XSSFBUtils.java
@@ -47,15 +47,14 @@ public class XSSFBUtils {
throw new XSSFBParseException("too many chars to read");
}
- int numBytes = 2*(int)numChars;
+ long numBytes = 2L*numChars;
offset += 4;
if (offset+numBytes > data.length) {
throw new XSSFBParseException("trying to read beyond data length:
" +
"offset="+offset+", numBytes="+numBytes+",
data.length="+data.length);
}
- sb.append(new String(data, offset, numBytes,
StandardCharsets.UTF_16LE));
- numBytes+=4;
- return numBytes;
+ sb.append(new String(data, offset, Math.toIntExact(numBytes),
StandardCharsets.UTF_16LE));
+ return Math.toIntExact(numBytes+4);
}
@@ -74,14 +73,13 @@ public class XSSFBUtils {
} else if (numChars > 0xFFFFFFFFL) {
throw new XSSFBParseException("too many chars to read");
}
- int numBytes = 2*(int)numChars;
+ long numBytes = 2L*numChars;
offset += 4;
if (offset+numBytes > data.length) {
throw new XSSFBParseException("trying to read beyond data length");
}
- sb.append(new String(data, offset, numBytes,
StandardCharsets.UTF_16LE));
- numBytes+=4;
- return numBytes;
+ sb.append(new String(data, offset, Math.toIntExact(numBytes),
StandardCharsets.UTF_16LE));
+ return Math.toIntExact(numBytes+4);
}
diff --git
a/poi-ooxml/src/test/java/org/apache/poi/xssf/binary/TestXSSFBUtils.java
b/poi-ooxml/src/test/java/org/apache/poi/xssf/binary/TestXSSFBUtils.java
new file mode 100644
index 0000000000..2856f9f189
--- /dev/null
+++ b/poi-ooxml/src/test/java/org/apache/poi/xssf/binary/TestXSSFBUtils.java
@@ -0,0 +1,72 @@
+/* ====================================================================
+ 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.xssf.binary;
+
+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;
+
+class TestXSSFBUtils {
+
+ /**
+ * A char count whose doubled byte length exceeds Integer.MAX_VALUE used to
+ * be narrowed via {@code 2*(int)numChars}, wrapping to a negative value
that
+ * slipped past the {@code offset+numBytes > data.length} check and
surfaced
+ * as a bare StringIndexOutOfBoundsException. The bounds check must reject
it
+ * with an XSSFBParseException instead.
+ */
+ @Test
+ void rejectsOversizedWideString() {
+ byte[] data = new byte[100];
+ LittleEndian.putUInt(data, 0, 0x40000000L); // ~1G chars -> 2G bytes
+
+ assertThrows(XSSFBParseException.class,
+ () -> XSSFBUtils.readXLWideString(data, 0, new
StringBuilder()));
+ assertThrows(XSSFBParseException.class,
+ () -> XSSFBUtils.readXLNullableWideString(data, 0, new
StringBuilder()));
+ }
+
+ /**
+ * A char count above 0x7FFFFFFF used to overflow to a small positive
+ * numBytes, silently reading the wrong length and desyncing the record
+ * stream. It must be rejected by the same bounds check.
+ */
+ @Test
+ void rejectsHighBitWideString() {
+ byte[] data = new byte[100];
+ LittleEndian.putUInt(data, 0, 0x80000001L);
+
+ assertThrows(XSSFBParseException.class,
+ () -> XSSFBUtils.readXLWideString(data, 0, new
StringBuilder()));
+ }
+
+ @Test
+ void readsValidWideString() throws Exception {
+ byte[] str =
"POI".getBytes(java.nio.charset.StandardCharsets.UTF_16LE);
+ byte[] data = new byte[4 + str.length];
+ LittleEndian.putUInt(data, 0, 3L);
+ System.arraycopy(str, 0, data, 4, str.length);
+
+ StringBuilder sb = new StringBuilder();
+ int read = XSSFBUtils.readXLWideString(data, 0, sb);
+ assertEquals("POI", sb.toString());
+ assertEquals(data.length, read);
+ }
+}
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]