src/lib/VSDXMLHelper.cpp | 6 ++- src/test/Makefile.am | 5 ++ src/test/VSDXMLHelperTest.cpp | 73 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 82 insertions(+), 2 deletions(-)
New commits: commit 8a326b11a267bd6daa0d1d5b9cbe027cbc08a1bd Author: Caolán McNamara <[email protected]> AuthorDate: Fri May 22 10:00:46 2026 +0100 Commit: Caolán McNamara <[email protected]> CommitDate: Fri May 22 11:10:31 2026 +0200 guard against a malformed Target with excess .. segments Change-Id: I04cbe356e4263b74d882932751d72a9c607c1ec6 Reviewed-on: https://gerrit.libreoffice.org/c/libvisio/+/205519 Reviewed-by: Caolán McNamara <[email protected]> Tested-by: Caolán McNamara <[email protected]> diff --git a/src/lib/VSDXMLHelper.cpp b/src/lib/VSDXMLHelper.cpp index 24045b6..9cc198b 100644 --- a/src/lib/VSDXMLHelper.cpp +++ b/src/lib/VSDXMLHelper.cpp @@ -66,7 +66,11 @@ void libvisio::VSDXRelationship::rebaseTarget(const char *baseDir) for (auto &segment : segments) { if (segment == "..") - normalizedSegments.pop_back(); + { + // guard against a malformed Target with excess ".." segments + if (!normalizedSegments.empty()) + normalizedSegments.pop_back(); + } else if (segment != "." && !segment.empty()) normalizedSegments.push_back(segment); } diff --git a/src/test/Makefile.am b/src/test/Makefile.am index 97b7e83..6f0928c 100644 --- a/src/test/Makefile.am +++ b/src/test/Makefile.am @@ -33,6 +33,7 @@ importtest_SOURCES = \ unittest_CPPFLAGS = \ -I$(top_srcdir)/src/lib \ $(LIBVISIO_CXXFLAGS) \ + $(REVENGE_STREAM_CFLAGS) \ $(CPPUNIT_CFLAGS) \ $(DEBUG_CXXFLAGS) @@ -41,10 +42,12 @@ unittest_LDADD = \ $(top_builddir)/src/lib/libvisio-internal.la \ libtest_driver.la \ $(LIBVISIO_LIBS) \ + $(REVENGE_STREAM_LIBS) \ $(CPPUNIT_LIBS) unittest_SOURCES = \ - VSDInternalStreamTest.cpp + VSDInternalStreamTest.cpp \ + VSDXMLHelperTest.cpp EXTRA_DIST = \ data/Visio11FormatLine.vsd \ diff --git a/src/test/VSDXMLHelperTest.cpp b/src/test/VSDXMLHelperTest.cpp new file mode 100644 index 0000000..5adfe9c --- /dev/null +++ b/src/test/VSDXMLHelperTest.cpp @@ -0,0 +1,73 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* + * This file is part of the libvisio project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#include <cppunit/TestFixture.h> +#include <cppunit/extensions/HelperMacros.h> + +#include <librevenge-stream/librevenge-stream.h> + +#include "VSDXMLHelper.h" + +namespace test +{ + +class VSDXMLHelperTest : public CPPUNIT_NS::TestFixture +{ +public: + virtual void setUp(); + virtual void tearDown(); + +private: + CPPUNIT_TEST_SUITE(VSDXMLHelperTest); + CPPUNIT_TEST(testRebaseTargetOverPop); + CPPUNIT_TEST_SUITE_END(); + +private: + void testRebaseTargetOverPop(); +}; + +void VSDXMLHelperTest::setUp() +{ +} + +void VSDXMLHelperTest::tearDown() +{ +} + +// A Target whose ".." segments would walk past the base directory must not +// crash. Previously the segment walk called pop_back() on an empty vector, +// which is undefined behaviour and triggered a heap-use-after-free. +void VSDXMLHelperTest::testRebaseTargetOverPop() +{ + static const char rels[] = + "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>" + "<Relationships xmlns=\"http://schemas.openxmlformats.org/package/2006/relationships\">" + "<Relationship Id=\"rId1\"" + " Type=\"http://schemas.microsoft.com/visio/2010/relationships/page\"" + " Target=\"../../trigger\"/>" + "</Relationships>"; + + librevenge::RVNGStringStream input(reinterpret_cast<const unsigned char *>(rels), + sizeof(rels) - 1); + libvisio::VSDXRelationships parsed(&input); + parsed.rebaseTargets("visio"); + + const libvisio::VSDXRelationship *r = parsed.getRelationshipById("rId1"); + CPPUNIT_ASSERT(r != nullptr); + // The over-deep ".." segments are silently dropped; what survives is the + // tail of the path. The exact result is not the point of the test - the + // point is that the rebase did not crash. + CPPUNIT_ASSERT_EQUAL(std::string("trigger"), r->getTarget()); +} + +CPPUNIT_TEST_SUITE_REGISTRATION(VSDXMLHelperTest); + +} + +/* vim:set shiftwidth=2 softtabstop=2 expandtab: */
