CVE-2026-6040
authorDebian LibreOffice Maintainers <debian-openoffice@lists.debian.org>
Sat, 6 Jun 2026 20:12:08 +0000 (22:12 +0200)
committerRene Engelhard <rene@debian.org>
Sat, 6 Jun 2026 20:12:08 +0000 (22:12 +0200)
CVE-2026-6040: ODT use-after-free in lcl_InsertBlankWidthChars

oss-fuzz efforts might not have found this because the fuzzer
dictionary was based on OpenDocument-v1.3-schema.rng and the
loext:blank-width-char isn't in that schema, adding in the extra
extension schema might help for the future.

From 997ef5c01cedc4a4f8b966310d4a79906009735e Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Caol=C3=A1n=20McNamara?= <caolan.mcnamara@collabora.com>
Date: Thu, 9 Apr 2026 17:47:09 +0100
Subject: [PATCH] process loext:blank-width-char better

Change-Id: Iea005facd85443091c5144a0a0f8f15fa995dbf3
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/203576
Reviewed-by: Xisco Fauli <xiscofauli@libreoffice.org>
Tested-by: Jenkins
Signed-off-by: Xisco Fauli <xiscofauli@libreoffice.org>
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/203627
Signed-off-by: Xisco Fauli <xiscofauli@libreoffice.org>
Gbp-Pq: Name CVE-2026-6040.diff

bin/oss-fuzz-setup.sh
xmloff/source/style/xmlnumfi.cxx

index 565f972948e23c27d6df7ffe2ff6fdcf3ad9c554..17b327a2ead8a0b494dbbef043a66059c1f0dedf 100755 (executable)
@@ -94,12 +94,13 @@ curl --no-progress-meter -S \
     -C - -O https://raw.githubusercontent.com/google/fuzzing/master/dictionaries/mathml.dict
 # build our own fuzz dict for odf, following the pattern of svg.dict
 echo "# Keywords taken from libreoffice/schema/odf1.3/OpenDocument-v1.3-schema.rng" > odf.dict
+echo "# and libreoffice/schema/libreoffice/OpenDocument-v1.4+libreoffice-schema.rng" >> odf.dict
 echo "# tags" >> odf.dict
-grep "rng:element name=" libreoffice/schema/odf1.3/OpenDocument-v1.3-schema.rng | sed 's#<rng:element name="#"<#;s#^[[:blank:]]*##;s#[[:blank:]>]*$##' >> odf.dict
+grep -h "rng:element name=" libreoffice/schema/odf1.3/OpenDocument-v1.3-schema.rng libreoffice/schema/libreoffice/OpenDocument-v1.4+libreoffice-schema.rng | sed 's#<rng:element name="#"<#;s#^[[:blank:]]*##;s#[[:blank:]>]*$##' >> odf.dict
 echo "# attributes " >> odf.dict
-grep "rng:attribute name=" libreoffice/schema/odf1.3/OpenDocument-v1.3-schema.rng | sed 's#<rng:attribute name="#"#;s#^[[:blank:]]*##;s#[[:blank:]>]*$##' >> odf.dict
+grep -h "rng:attribute name=" libreoffice/schema/odf1.3/OpenDocument-v1.3-schema.rng libreoffice/schema/libreoffice/OpenDocument-v1.4+libreoffice-schema.rng | sed 's#<rng:attribute name="#"#;s#^[[:blank:]]*##;s#[[:blank:]>]*$##' >> odf.dict
 echo "# attributes' values" >> odf.dict
-grep "rng:value" libreoffice/schema/odf1.3/OpenDocument-v1.3-schema.rng | sed 's#<rng:value>#"#;s#</rng:value>#"#;s#^[[:blank:]]*##;s#[[:blank:]>]*$##' | sort | uniq >> odf.dict
+grep -h "rng:value" libreoffice/schema/odf1.3/OpenDocument-v1.3-schema.rng libreoffice/schema/libreoffice/OpenDocument-v1.4+libreoffice-schema.rng | sed 's#<rng:value>#"#;s#</rng:value>#"#;s#^[[:blank:]]*##;s#[[:blank:]>]*$##' | sort | uniq >> odf.dict
 
 #fuzzing corpuses
 #afl jpeg, gif, bmp, png, webp
index 110300ac1828405412c8d99273d459f99e461cf4..347c47fd9bfea6d1eb2b6ed4f802ead31c14dcdf 100644 (file)
@@ -903,7 +903,7 @@ void lcl_InsertBlankWidthChars( std::u16string_view rBlankWidthString, OUStringB
                 nPositionContent = o3tl::toInt32( rBlankWidthString.substr( i ) );
         }
         nPositionContent += nShiftPosition;
-        if ( nPositionContent >= 0 )
+        if ( nPositionContent >= 0 && nPositionContent <= rContent.getLength() )
         {
             rContent.remove( nPositionContent, aBlanks.getLength() );
             if ( nPositionContent >= 1 && rContent[ nPositionContent-1 ] == '\"' )
@@ -924,11 +924,10 @@ void lcl_InsertBlankWidthChars( std::u16string_view rBlankWidthString, OUStringB
         }
     }
     // remove empty string at the end of rContent
-    if ( std::u16string_view( rContent ).substr( rContent.getLength() - 2 ) == u"\"\"" )
+    sal_Int32 nLen = rContent.getLength();
+    if ( nLen >= 3 && std::u16string_view( rContent ).substr( nLen - 2 ) == u"\"\"" && rContent[ nLen-3 ] != '\\' )
     {
-        sal_Int32 nLen = rContent.getLength();
-        if ( nLen >= 3 && rContent[ nLen-3 ] != '\\' )
-            rContent.truncate( nLen - 2 );
+        rContent.truncate( nLen - 2 );
     }
 }
 }