src/lib/VSDXParser.cpp              |    4 ++--
 src/test/Makefile.am                |    3 ++-
 src/test/data/tab-short-prefix.vsdx |binary
 src/test/importtest.cpp             |   21 +++++++++++++++++++++
 4 files changed, 25 insertions(+), 3 deletions(-)

New commits:
commit 724c22ee8a999293bf13838e72923b001ae5b352
Author:     Caolán McNamara <[email protected]>
AuthorDate: Fri May 22 12:50:19 2026 +0000
Commit:     Caolán McNamara <[email protected]>
CommitDate: Fri May 22 14:53:36 2026 +0200

    keep inside buffer readTabRow on short N attribute
    
    Guard the pointer arithmetic on xmlStrlen first.
    
    Change-Id: I8d400bc287a77235f3ac497e4afac88169034a6b
    Reviewed-on: https://gerrit.libreoffice.org/c/libvisio/+/205556
    Reviewed-by: Caolán McNamara <[email protected]>
    Tested-by: Caolán McNamara <[email protected]>

diff --git a/src/lib/VSDXParser.cpp b/src/lib/VSDXParser.cpp
index ba977ad..4265843 100644
--- a/src/lib/VSDXParser.cpp
+++ b/src/lib/VSDXParser.cpp
@@ -1455,7 +1455,7 @@ void libvisio::VSDXParser::readTabRow(xmlTextReaderPtr 
reader)
         if (XML_READER_TYPE_ELEMENT == tokenType)
         {
           const std::shared_ptr<xmlChar> 
stringValue(xmlTextReaderGetAttribute(reader, BAD_CAST("N")), xmlFree);
-          if (stringValue)
+          if (stringValue && xmlStrlen(stringValue.get()) > 8)
           {
             unsigned idx = xmlStringToLong(stringValue.get()+8);
             ret = readDoubleData((*m_currentTabSet)[idx].m_position, reader);
@@ -1466,7 +1466,7 @@ void libvisio::VSDXParser::readTabRow(xmlTextReaderPtr 
reader)
         if (XML_READER_TYPE_ELEMENT == tokenType)
         {
           const std::shared_ptr<xmlChar> 
stringValue(xmlTextReaderGetAttribute(reader, BAD_CAST("N")), xmlFree);
-          if (stringValue)
+          if (stringValue && xmlStrlen(stringValue.get()) > 9)
           {
             unsigned idx = xmlStringToLong(stringValue.get()+9);
             ret = readByteData((*m_currentTabSet)[idx].m_alignment, reader);
diff --git a/src/test/Makefile.am b/src/test/Makefile.am
index 7889ede..4fc6a5d 100644
--- a/src/test/Makefile.am
+++ b/src/test/Makefile.am
@@ -90,7 +90,8 @@ EXTRA_DIST = \
        data/testfile4.vsdx \
        data/testfile5.vsdx \
        data/testfile6.vsdx \
-       data/recursion-cycle.vsdx
+       data/recursion-cycle.vsdx \
+       data/tab-short-prefix.vsdx
 
 # ImportTest::testVsdMetadataTitleUtf8 checks formatted date string
 AM_TESTS_ENVIRONMENT = TZ=UTC; export TZ;
diff --git a/src/test/data/tab-short-prefix.vsdx 
b/src/test/data/tab-short-prefix.vsdx
new file mode 100644
index 0000000..20237f1
Binary files /dev/null and b/src/test/data/tab-short-prefix.vsdx differ
diff --git a/src/test/importtest.cpp b/src/test/importtest.cpp
index 4054929..5de2cf9 100644
--- a/src/test/importtest.cpp
+++ b/src/test/importtest.cpp
@@ -236,6 +236,7 @@ class ImportTest : public CPPUNIT_NS::TestFixture
   CPPUNIT_TEST(testVsdxFillStylesFromTheme6);
 
   CPPUNIT_TEST(testVsdxPageSelfReferenceCycle);
+  CPPUNIT_TEST(testVsdxTabRowShortPrefix);
 
   CPPUNIT_TEST_SUITE_END();
 
@@ -270,6 +271,7 @@ class ImportTest : public CPPUNIT_NS::TestFixture
   void testVsdxFillStylesFromTheme6();
 
   void testVsdxPageSelfReferenceCycle();
+  void testVsdxTabRowShortPrefix();
 
   xmlBufferPtr m_buffer;
   xmlDocPtr m_doc;
@@ -657,6 +659,25 @@ void ImportTest::testVsdxFillStylesFromTheme6()
   assertXPath(m_doc, "/document/page/layer[3]//setStyle[2]", "fill-color", 
"#feffff");
 }
 
+// N="X" on <Position>/<Alignment> is shorter than the "Position"/"Alignment"
+// prefix the code skips with +8/+9 - must not read past the xmlChar buffer
+void ImportTest::testVsdxTabRowShortPrefix()
+{
+  librevenge::RVNGString path(TDOC "/tab-short-prefix.vsdx");
+  librevenge::RVNGFileStream input(path.cstr());
+  CPPUNIT_ASSERT(libvisio::VisioDocument::isSupported(&input));
+
+  xmlTextWriterPtr writer = xmlNewTextWriterMemory(m_buffer, 0);
+  CPPUNIT_ASSERT(writer);
+  xmlTextWriterStartDocument(writer, 0, 0, 0);
+  libvisio::XmlDrawingGenerator painter(writer);
+
+  (void)libvisio::VisioDocument::parse(&input, &painter);
+
+  xmlTextWriterEndDocument(writer);
+  xmlFreeTextWriter(writer);
+}
+
 // pages.xml.rels points at pages.xml - must not recurse forever
 void ImportTest::testVsdxPageSelfReferenceCycle()
 {

Reply via email to