tools/qa/cppunit/test_stream.cxx | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+)
New commits: commit 9b4077998d32d738737da9ef6d3c26129075779a Author: Stephan Bergmann <[email protected]> AuthorDate: Wed Feb 25 20:58:49 2026 +0100 Commit: Stephan Bergmann <[email protected]> CommitDate: Thu Feb 26 17:45:50 2026 +0100 Add test ...for 0468b71c3f284ad6f7826aee79b8adb1998fc018 "fix asan heap-use-after-free" Change-Id: I4045a2a3635d628d7f681406bd6a8b9f26fa23d3 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/200351 Reviewed-by: Stephan Bergmann <[email protected]> Tested-by: Jenkins diff --git a/tools/qa/cppunit/test_stream.cxx b/tools/qa/cppunit/test_stream.cxx index 4390fe0c6e16..b1e8ec3402f8 100644 --- a/tools/qa/cppunit/test_stream.cxx +++ b/tools/qa/cppunit/test_stream.cxx @@ -13,6 +13,8 @@ #include <cppunit/extensions/HelperMacros.h> #include <tools/stream.hxx> #include <unotools/tempfile.hxx> +#include <cstddef> +#include <cstring> #include <sstream> //Tests for eofbit/badbit/goodbit/failbit @@ -30,6 +32,7 @@ namespace void test_readline(); void test_makereadonly(); void test_write_unicode(); + void test_write_from_self(); CPPUNIT_TEST_SUITE(Test); CPPUNIT_TEST(test_stdstream); @@ -39,6 +42,7 @@ namespace CPPUNIT_TEST(test_readline); CPPUNIT_TEST(test_makereadonly); CPPUNIT_TEST(test_write_unicode); + CPPUNIT_TEST(test_write_from_self); CPPUNIT_TEST_SUITE_END(); }; @@ -348,6 +352,34 @@ namespace } } + // Regression test for a heap-use-after-free where PutData reads from a dangling pointer after + // ReAllocateMemory freed the old buffer: + void Test::test_write_from_self() + { + // Use a tiny initial size and resize offset so we can easily force a reallocation: + SvMemoryStream stream(16, 16); + // Disable SvStream's internal buffering so that WriteBytes calls PutData directly with our + // pointer, rather than copying into an intermediate buffer: + stream.SetBufferSize(0); + constexpr std::size_t patternLen = 15; + char const pattern[patternLen + 1] = "0123456789ABCDE"; + stream.WriteBytes(pattern, patternLen); // nearly fills 16-byte buffer + CPPUNIT_ASSERT_EQUAL(ERRCODE_NONE, stream.GetError()); + // Writing 15 bytes from the internal pointer will exceed the current + // 16-byte buffer and force a reallocation: + stream.WriteBytes(stream.GetData(), patternLen); + CPPUNIT_ASSERT_EQUAL(ERRCODE_NONE, stream.GetError()); + CPPUNIT_ASSERT_EQUAL(sal_uInt64(patternLen * 2), stream.GetSize()); + stream.Seek(0); + CPPUNIT_ASSERT_EQUAL(ERRCODE_NONE, stream.GetError()); + char result[patternLen * 2]; + std::size_t const nRead = stream.ReadBytes(result, patternLen * 2); + CPPUNIT_ASSERT_EQUAL(ERRCODE_NONE, stream.GetError()); + CPPUNIT_ASSERT_EQUAL(patternLen * 2, nRead); + CPPUNIT_ASSERT_EQUAL(0, std::memcmp(result, pattern, patternLen)); + CPPUNIT_ASSERT_EQUAL(0, std::memcmp(result + patternLen, pattern, patternLen)); + } + CPPUNIT_TEST_SUITE_REGISTRATION(Test); }
