49 lines
2 KiB
Diff
49 lines
2 KiB
Diff
# UNDF: UNDF-2026-000000786
|
|
# UNDF: (leave blank)
|
|
# LibreOffice CWE-407: SwWW8WrGrf::Write() O(N^2) graphic dedup
|
|
#
|
|
# In sw/source/filter/ww8/wrtww8gr.cxx, the Write() method iterates over
|
|
# maDetails and for each entry searches backward from begin to current
|
|
# position via std::find() to detect duplicate graphics. This is O(N^2)
|
|
# where N = number of graphics in the document.
|
|
#
|
|
# Fix: use an unordered_map to track previously-seen details, reducing
|
|
# the dedup lookup from O(N) to O(1) amortized, making Write() O(N) total.
|
|
#
|
|
# Severity: MEDIUM — documents with hundreds of embedded graphics
|
|
# (e.g. mail-merge templates, catalogs) trigger quadratic export time.
|
|
# At N=500 graphics, ~125,000 comparisons instead of 500.
|
|
--- a/sw/source/filter/ww8/wrtww8gr.cxx
|
|
+++ b/sw/source/filter/ww8/wrtww8gr.cxx
|
|
@@ -864,13 +864,18 @@
|
|
void SwWW8WrGrf::Write()
|
|
{
|
|
SvStream& rStrm = *m_rWrt.m_pDataStrm;
|
|
+ // Map from detail hash to first occurrence index for O(1) dedup lookup
|
|
+ std::unordered_map<size_t, size_t> aSeenMap;
|
|
auto aEnd = maDetails.end();
|
|
- for (auto aIter = maDetails.begin(); aIter != aEnd; ++aIter)
|
|
+ for (auto aIter = maDetails.begin(); aIter != aEnd; ++aIter)
|
|
{
|
|
sal_uInt64 nPos = rStrm.Tell(); // align to 4 Bytes
|
|
if( nPos & 0x3 )
|
|
SwWW8Writer::FillCount( rStrm, 4 - ( nPos & 0x3 ) );
|
|
|
|
- auto aIter2 = std::find(maDetails.begin(), aIter, *aIter);
|
|
- if (aIter2 != aIter)
|
|
+ size_t nIdx = static_cast<size_t>(aIter - maDetails.begin());
|
|
+ size_t nHash = std::hash<ww8::Frame>{}(aIter->maFly);
|
|
+ auto aFound = aSeenMap.find(nHash);
|
|
+ if (aFound != aSeenMap.end() && maDetails[aFound->second] == *aIter)
|
|
{
|
|
- aIter->mnPos = aIter2->mnPos;
|
|
+ aIter->mnPos = maDetails[aFound->second].mnPos;
|
|
}
|
|
else
|
|
{
|
|
aIter->mnPos = rStrm.Tell();
|
|
WriteGraphicNode(rStrm, *aIter);
|
|
+ aSeenMap[nHash] = nIdx;
|
|
}
|
|
}
|
|
}
|