crashtesting: clamp string concatenation to each matrix operand's size

Concatenating two matrices of different sizes gives a result the size
of the larger one.

Each operand was read across the whole result. The shorter operand
has fewer cells than the result, so the read ran past the operand's
end and went out of bounds, which crashed. Now each operand is read
only within its own size.

A cell that only the larger operand covers has no partner in the
shorter one to join with, so it stays empty.

a problem since:

commit b4957ed105bcaa4a73666a6a5e119d523284b0c9
Date:   Sat Jun 27 19:26:56 2026 +0900

    sc: extend mismatched-extent matrix arithmetic to the longer side

Signed-off-by: Caolán McNamara <caolan.mcnamara@collabora.com>
Change-Id: I0ae4a0b71a846bb88383cd1cb472a022895ae14a
Reviewed-on: https://gerrit.collaboraoffice.com/c/online/+/6696
Tested-by: Jenkins CPCI <releng@collaboraoffice.com>
Reviewed-by: Tomaž Vajngerl <tomaz.vajngerl@collabora.com>
(cherry picked from commit 645e103b914f07f80c0c425fb1b3262e5e4f1648)
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/208007
Reviewed-by: Xisco Fauli <xiscofauli@libreoffice.org>
Tested-by: Jenkins
This commit is contained in:
Caolán McNamara
2026-07-13 11:41:28 +02:00
committed by Xisco Fauli
parent ed9864df27
commit 35ddb41dcc
2 changed files with 40 additions and 2 deletions
+38
View File
@@ -2273,6 +2273,44 @@ CPPUNIT_TEST_FIXTURE(TestFormula2, testMatrixArithmeticMismatchedExtent)
m_pDoc->DeleteTab(0);
}
CPPUNIT_TEST_FIXTURE(TestFormula2, testMatrixConcatMismatchedExtent)
{
// String concatenation of two two-dimensional ranges of unequal
// length extends the result to the longer range. Where the two
// ranges overlap the cells join together. The rows past the shorter
// operand have no partner to join and stay empty.
sc::AutoCalcSwitch aACSwitch(*m_pDoc, true);
m_pDoc->InsertTab(0, u"Test"_ustr);
// First operand A1:B3, three rows by two columns.
m_pDoc->SetString(ScAddress(0, 0, 0), u"a"_ustr);
m_pDoc->SetString(ScAddress(1, 0, 0), u"b"_ustr);
m_pDoc->SetString(ScAddress(0, 1, 0), u"c"_ustr);
m_pDoc->SetString(ScAddress(1, 1, 0), u"d"_ustr);
m_pDoc->SetString(ScAddress(0, 2, 0), u"e"_ustr);
m_pDoc->SetString(ScAddress(1, 2, 0), u"f"_ustr);
// Second operand D1:E2, two rows by two columns, one row shorter.
m_pDoc->SetString(ScAddress(3, 0, 0), u"1"_ustr);
m_pDoc->SetString(ScAddress(4, 0, 0), u"2"_ustr);
m_pDoc->SetString(ScAddress(3, 1, 0), u"3"_ustr);
m_pDoc->SetString(ScAddress(4, 1, 0), u"4"_ustr);
ScMarkData aMark(m_pDoc->GetSheetLimits());
aMark.SelectOneTable(0);
m_pDoc->InsertMatrixFormula(6, 0, 7, 2, aMark, u"=A1:B3&D1:E2"_ustr);
CPPUNIT_ASSERT_EQUAL(u"a1"_ustr, m_pDoc->GetString(ScAddress(6, 0, 0)));
CPPUNIT_ASSERT_EQUAL(u"b2"_ustr, m_pDoc->GetString(ScAddress(7, 0, 0)));
CPPUNIT_ASSERT_EQUAL(u"c3"_ustr, m_pDoc->GetString(ScAddress(6, 1, 0)));
CPPUNIT_ASSERT_EQUAL(u"d4"_ustr, m_pDoc->GetString(ScAddress(7, 1, 0)));
// The third row is past the second operand, so those cells stay empty.
CPPUNIT_ASSERT_EQUAL(OUString(), m_pDoc->GetString(ScAddress(6, 2, 0)));
CPPUNIT_ASSERT_EQUAL(OUString(), m_pDoc->GetString(ScAddress(7, 2, 0)));
m_pDoc->DeleteTab(0);
}
CPPUNIT_TEST_FIXTURE(TestFormula2, testMatrixOp)
{
m_pDoc->InsertTab(0, u"Test"_ustr);
+2 -2
View File
@@ -2888,7 +2888,7 @@ void ScMatrixImpl::MatConcat(SCSIZE nMaxCol, SCSIZE nMaxRow, const ScMatrixRef&
else
xMat1->ExecuteOperation(
std::pair<size_t, size_t>(0, 0),
std::pair<size_t, size_t>(nMaxRow - 1, nMaxCol - 1),
std::pair<size_t, size_t>(std::min(nR1, nMaxRow) - 1, std::min(nC1, nMaxCol) - 1),
std::move(aDoubleFunc), std::move(aBoolFunc), std::move(aStringFunc), std::move(aEmptyFunc));
std::vector<svl::SharedString> aSharedString(nMaxCol*nMaxRow);
@@ -2952,7 +2952,7 @@ void ScMatrixImpl::MatConcat(SCSIZE nMaxCol, SCSIZE nMaxRow, const ScMatrixRef&
else
xMat2->ExecuteOperation(
std::pair<size_t, size_t>(0, 0),
std::pair<size_t, size_t>(nMaxRow - 1, nMaxCol - 1),
std::pair<size_t, size_t>(std::min(nR2, nMaxRow) - 1, std::min(nC2, nMaxCol) - 1),
std::move(aDoubleFunc2), std::move(aBoolFunc2), std::move(aStringFunc2), std::move(aEmptyFunc2));
aString.clear();