src/lib/VSDXParser.cpp | 40 +++++++++++++++++++++++++++++++------ src/lib/VSDXParser.h | 4 +++ src/test/Makefile.am | 3 +- src/test/data/recursion-cycle.vsdx |binary src/test/importtest.cpp | 22 ++++++++++++++++++++ 5 files changed, 62 insertions(+), 7 deletions(-)
New commits: commit ec5dc2dcbb97006cd1d2dbbee608934b08c33b04 Author: Caolán McNamara <[email protected]> AuthorDate: Fri May 22 09:49:02 2026 +0000 Commit: Caolán McNamara <[email protected]> CommitDate: Fri May 22 13:07:09 2026 +0200 avoid infinite depth page/master relationships parse Track per-target visited parts on the parsePage / parseMaster recursion edge and refuse to re-enter a part already on the stack. Same shape as the visited-set of: commit d095eb768180f86df4f939d86f3debf3a7078872 Date: Mon May 15 16:39:58 2017 +0200 ofz#1000 avoid stack overflow Change-Id: I26a864bba701fd03b4e36d6fcea087933428a411 Reviewed-on: https://gerrit.libreoffice.org/c/libvisio/+/205554 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 4d47f6e..ba977ad 100644 --- a/src/lib/VSDXParser.cpp +++ b/src/lib/VSDXParser.cpp @@ -335,15 +335,43 @@ void libvisio::VSDXParser::processXmlDocument(librevenge::RVNGInputStream *input std::string type = rel->getType(); if (type == "http://schemas.microsoft.com/visio/2010/relationships/master") { - m_currentDepth += xmlTextReaderDepth(reader.get()); - parseMaster(m_input, rel->getTarget().c_str()); - m_currentDepth -= xmlTextReaderDepth(reader.get()); + const std::string target = rel->getTarget(); + const auto inserted = m_visitedParts.insert(target); + if (inserted.second) + { + m_currentDepth += xmlTextReaderDepth(reader.get()); + try + { + parseMaster(m_input, target.c_str()); + } + catch (...) + { + m_visitedParts.erase(inserted.first); + throw; + } + m_currentDepth -= xmlTextReaderDepth(reader.get()); + m_visitedParts.erase(inserted.first); + } } else if (type == "http://schemas.microsoft.com/visio/2010/relationships/page") { - m_currentDepth += xmlTextReaderDepth(reader.get()); - parsePage(m_input, rel->getTarget().c_str()); - m_currentDepth -= xmlTextReaderDepth(reader.get()); + const std::string target = rel->getTarget(); + const auto inserted = m_visitedParts.insert(target); + if (inserted.second) + { + m_currentDepth += xmlTextReaderDepth(reader.get()); + try + { + parsePage(m_input, target.c_str()); + } + catch (...) + { + m_visitedParts.erase(inserted.first); + throw; + } + m_currentDepth -= xmlTextReaderDepth(reader.get()); + m_visitedParts.erase(inserted.first); + } } else if (type == "http://schemas.openxmlformats.org/officeDocument/2006/relationships/image") { diff --git a/src/lib/VSDXParser.h b/src/lib/VSDXParser.h index d8d0686..4b48814 100644 --- a/src/lib/VSDXParser.h +++ b/src/lib/VSDXParser.h @@ -10,6 +10,8 @@ #ifndef __VSDXPARSER_H__ #define __VSDXPARSER_H__ +#include <set> +#include <string> #include <librevenge/librevenge.h> #include "VSDXTheme.h" #include "VSDXMLParserBase.h" @@ -86,6 +88,8 @@ private: int m_currentDepth; VSDXRelationships *m_rels; VSDXTheme m_currentTheme; + // parsePage / parseMaster targets currently on the recursion stack + std::set<std::string> m_visitedParts; }; } // namespace libvisio diff --git a/src/test/Makefile.am b/src/test/Makefile.am index 6f0928c..7889ede 100644 --- a/src/test/Makefile.am +++ b/src/test/Makefile.am @@ -89,7 +89,8 @@ EXTRA_DIST = \ data/testfile3.vsdx \ data/testfile4.vsdx \ data/testfile5.vsdx \ - data/testfile6.vsdx + data/testfile6.vsdx \ + data/recursion-cycle.vsdx # ImportTest::testVsdMetadataTitleUtf8 checks formatted date string AM_TESTS_ENVIRONMENT = TZ=UTC; export TZ; diff --git a/src/test/data/recursion-cycle.vsdx b/src/test/data/recursion-cycle.vsdx new file mode 100644 index 0000000..fc74677 Binary files /dev/null and b/src/test/data/recursion-cycle.vsdx differ diff --git a/src/test/importtest.cpp b/src/test/importtest.cpp index 2d44159..4054929 100644 --- a/src/test/importtest.cpp +++ b/src/test/importtest.cpp @@ -235,6 +235,8 @@ class ImportTest : public CPPUNIT_NS::TestFixture CPPUNIT_TEST(testVsdxFillStylesFromTheme5); CPPUNIT_TEST(testVsdxFillStylesFromTheme6); + CPPUNIT_TEST(testVsdxPageSelfReferenceCycle); + CPPUNIT_TEST_SUITE_END(); void testVsd6Textfields(); @@ -267,6 +269,8 @@ class ImportTest : public CPPUNIT_NS::TestFixture void testVsdxFillStylesFromTheme5(); void testVsdxFillStylesFromTheme6(); + void testVsdxPageSelfReferenceCycle(); + xmlBufferPtr m_buffer; xmlDocPtr m_doc; @@ -653,6 +657,24 @@ void ImportTest::testVsdxFillStylesFromTheme6() assertXPath(m_doc, "/document/page/layer[3]//setStyle[2]", "fill-color", "#feffff"); } +// pages.xml.rels points at pages.xml - must not recurse forever +void ImportTest::testVsdxPageSelfReferenceCycle() +{ + librevenge::RVNGString path(TDOC "/recursion-cycle.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); +} + CPPUNIT_TEST_SUITE_REGISTRATION(ImportTest); /* vim:set shiftwidth=2 softtabstop=2 expandtab: */
