Author: centic
Date: Mon May  5 17:23:59 2025
New Revision: 1925419

URL: http://svn.apache.org/viewvc?rev=1925419&view=rev
Log:
Bug 69667: Handle slightly broken WriteAccessRecord gracefully

It seems some software creates records with invalid length.

If it uses UTF-16LE encoding, we can end up with 109 bytes, 
which is invalid as UTF-16LE always requires an even number of 
bytes.

Therefor we now sanitize the number of bytes we read from the 
record to avoid this issue.

Also improve error message and add tests

Modified:
    
poi/trunk/poi/src/main/java/org/apache/poi/hssf/record/WriteAccessRecord.java
    
poi/trunk/poi/src/test/java/org/apache/poi/hssf/record/TestWriteAccessRecord.java

Modified: 
poi/trunk/poi/src/main/java/org/apache/poi/hssf/record/WriteAccessRecord.java
URL: 
http://svn.apache.org/viewvc/poi/trunk/poi/src/main/java/org/apache/poi/hssf/record/WriteAccessRecord.java?rev=1925419&r1=1925418&r2=1925419&view=diff
==============================================================================
--- 
poi/trunk/poi/src/main/java/org/apache/poi/hssf/record/WriteAccessRecord.java 
(original)
+++ 
poi/trunk/poi/src/main/java/org/apache/poi/hssf/record/WriteAccessRecord.java 
Mon May  5 17:23:59 2025
@@ -108,10 +108,19 @@ public final class WriteAccessRecord ext
             data = IOUtils.safelyAllocate(in.remaining(), STRING_SIZE);
             in.readFully(data);
             if (UTF16FLAG.isSet(is16BitFlag)) {
-                byteCnt = Math.min(nChars * 2, data.length);
+                // the spec only allows up to 109 bytes for the string in this 
record, but it seems some broken
+                // software out there will generate invalid records
+                int min = Math.min(nChars * 2, data.length);
+
+                // make sure byteCnt is divisible by 2 as we read UTF-16LE
+                byteCnt = min - (min % 2);
+
                 charset = StandardCharsets.UTF_16LE;
             } else {
+                // the spec only allows up to 109 bytes for the string in this 
record, but it seems some broken
+                // software out there will generate invalid records
                 byteCnt = Math.min(nChars, data.length);
+
                 charset = StandardCharsets.ISO_8859_1;
             }
         }
@@ -130,7 +139,8 @@ public final class WriteAccessRecord ext
         boolean is16bit = StringUtil.hasMultibyte(username);
         int encodedByteCount = username.length() * (is16bit ? 2 : 1);
         if (encodedByteCount > STRING_SIZE) {
-            throw new IllegalArgumentException("Name is too long: " + 
username);
+            throw new IllegalArgumentException("Name is too long, expecting up 
to " + STRING_SIZE +
+                    " bytes, but had: " + encodedByteCount + " bytes: " + 
username);
         }
 
         field_1_username = username;

Modified: 
poi/trunk/poi/src/test/java/org/apache/poi/hssf/record/TestWriteAccessRecord.java
URL: 
http://svn.apache.org/viewvc/poi/trunk/poi/src/test/java/org/apache/poi/hssf/record/TestWriteAccessRecord.java?rev=1925419&r1=1925418&r2=1925419&view=diff
==============================================================================
--- 
poi/trunk/poi/src/test/java/org/apache/poi/hssf/record/TestWriteAccessRecord.java
 (original)
+++ 
poi/trunk/poi/src/test/java/org/apache/poi/hssf/record/TestWriteAccessRecord.java
 Mon May  5 17:23:59 2025
@@ -93,4 +93,47 @@ final class TestWriteAccessRecord {
 
         confirmRecordEncoding(WriteAccessRecord.sid, expectedEncoding, 
rec.serialize());
     }
+
+    @Test
+    void testUTF16LE() {
+        byte[] data = HexRead.readFromString(""
+                + "5c 00 70 00 1C 00 01 "
+                + "44 00 61 00 74 00 61 00 20 00 44 00 79 00 6e 00 "
+                + "61 00 6d 00 69 00 63 00 73 00 27 00 20 00 53 00 "
+                + "70 00 72 00 65 00 61 00 64 00 42 00 75 00 69 00 "
+                + "6c 00 64 00 65 00 72 00 20 00 20 00 20 00 20 00 "
+                + "20 00 20 00 20 00 20 00 20 00 20 00 20 00 20 00 "
+                + "20 00 20 00 20 00 20 00 20 00 20 00 20 00 20 00 "
+                + "20 00 20 00 20 00 20 00 20 00 20 00 20 00 20 00 "
+                + "20 00 20 00 20 00"
+        );
+        RecordInputStream in = TestcaseRecordInputStream.create(data);
+
+        WriteAccessRecord rec = new WriteAccessRecord(in);
+
+        assertEquals("Data Dynamics' SpreadBuilder", rec.getUsername());
+    }
+
+    @Test
+    void testUTF16LE_wrong_size() {
+        // "0x51" on position 5 is an incorrect size, as it would require 162 
bytes to encode as UTF-16LE
+        // the spec only allows up to 109 bytes for the string in this record, 
but it seems some broken
+        // software out there will generate such a file
+        byte[] data = HexRead.readFromString(""
+                + "5c 00 70 00 51 00 01 "
+                + "44 00 61 00 74 00 61 00 20 00 44 00 79 00 6e 00 "
+                + "61 00 6d 00 69 00 63 00 73 00 27 00 20 00 53 00 "
+                + "70 00 72 00 65 00 61 00 64 00 42 00 75 00 69 00 "
+                + "6c 00 64 00 65 00 72 00 20 00 20 00 20 00 20 00 "
+                + "20 00 20 00 20 00 20 00 20 00 20 00 20 00 20 00 "
+                + "20 00 20 00 20 00 20 00 20 00 20 00 20 00 20 00 "
+                + "20 00 20 00 20 00 20 00 20 00 20 00 20 00 20 00 "
+                + "20 00 20 00 20 00"
+        );
+        RecordInputStream in = TestcaseRecordInputStream.create(data);
+
+        WriteAccessRecord rec = new WriteAccessRecord(in);
+
+        assertEquals("Data Dynamics' SpreadBuilder", rec.getUsername());
+    }
 }



---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to