Outreach docs (unblock intel page generation): - kdenlive: 10 defects (8 CWE-407 + 1 CWE-362 + 1 keyframe), C++ - libreoffice: 5 defects (Writer, Calc, SFX, Impress), C++ - maven: 7 defects (graph, lifecycle, sort-by-indexOf), Java - cpython: 7 defects (pkgutil, codegen, mock, pmerge MRO, pydoc), C/Python - blender: 4 defects (node runtime, USD skel, shader, anim), C++ Mastodon CWE-1333 benchmark: - test_mastodon_cwe1333.rb: validates (.+\.)? -> ([^@]+\.)? fix eliminates O(2^N) backtracking in email validator
5.5 KiB
LibreOffice — CWE-407 Disclosure Brief
2026-04-13 · Patches available — awaiting upstream merge
Finding
Five algorithmic complexity defects in LibreOffice across Writer document export, Calc pivot tables, slot pool registration, Writer table operations, and Impress outline view. All patched. Defects span three major LibreOffice applications (Writer, Calc, Impress) plus the shared SFX framework.
The Defects
libreoffice-0001 (PATCHED — MEDIUM): sw/source/filter/ww8/wrtww8gr.cxx — SwWW8WrGrf::Write()
// O(N^2) graphic dedup — std::find from begin to current position
auto aIter2 = std::find(maDetails.begin(), aIter, *aIter);
if (aIter2 != aIter) {
aIter->mnPos = aIter2->mnPos; // reuse duplicate graphic position
}
For each graphic in maDetails, searches backward from begin to current position via std::find(). O(N^2) where N = number of embedded graphics. Documents with hundreds of embedded graphics (mail-merge templates, catalogs) trigger quadratic export time.
libreoffice-0002 (PATCHED — MEDIUM-HIGH): sc/source/core/data/dpfilteredcache.cxx — GroupFilter::match()
// O(R*I) pivot table filter — std::find on maItems per row
return std::find(maItems.begin(), maItems.end(), rCellData) != maItems.end();
Called per-row by isRowQualified() during filterByPageDimension() and filterTable(). O(R*I) where R = rows and I = filter items. Pivot tables with thousands of rows and multi-value page filters hit this path on every recalc.
libreoffice-0003 (PATCHED — LOW-MEDIUM): sfx2/source/control/msgpool.cxx — SfxSlotPool registration
// O(F*G) group dedup — std::find on _vGroups per slot
if (std::find(_vGroups.begin(), _vGroups.end(), rDef.GetGroupId()) == _vGroups.end())
Iterates over all slots (F) and checks each GroupId against _vGroups via std::find(). O(F*G) where F = slots, G = groups. Fires once per interface registration at startup. At F=300 slots, G=50 groups: 15,000 comparisons.
libreoffice-0004 (PATCHED — MEDIUM): sw/source/core/docnode/ndtbl1.cxx — InsertLine()
// O(L^2) table line dedup — std::find before push_back
if (rLineArr.end() == std::find(rLineArr.begin(), rLineArr.end(), pLine))
rLineArr.push_back(pLine);
Called in a loop for every table line during merge, split, and selection operations. O(L^2) where L = table lines. At L=500 lines: 125,000 comparisons.
libreoffice-0005 (PATCHED — MEDIUM): sd/source/ui/view/outlview.cxx — BeginMovingHdl / SetSelectedPages
// O(P*S) selected paragraphs scan — std::find per paragraph
fiter = std::find(maSelectedParas.begin(), maSelectedParas.end(), pPara);
pPage->SetSelected(fiter != maSelectedParas.end());
Two methods scan maSelectedParas via std::find() inside a while-loop over all paragraphs. O(P*S) where P = total paragraphs, S = selected paragraphs. Fires during slide reordering and selection in Impress.
Complexity Proof
libreoffice-0001: At N=500 embedded graphics:
- Defective: ~125,000 comparisons during WW8 export
- Fixed: ~500 hash lookups
- 250x op reduction
libreoffice-0002: At R=10,000 rows, I=50 filter items:
- Defective: 500,000 comparisons per pivot recalc
- Fixed: 10,000 set lookups
- 50x op reduction
libreoffice-0004: At L=500 table lines:
- Defective: 125,000 comparisons
- Fixed: 500 set lookups
- 250x op reduction
libreoffice-0005: At P=500 paragraphs, S=100 selected:
- Defective: 50,000 comparisons
- Fixed: 500 set lookups
- 100x op reduction
Impact
LibreOffice is the most widely deployed open-source office suite, used by governments, educational institutions, and millions of individual users worldwide. libreoffice-0001 affects document export for any Writer document with many embedded images. libreoffice-0002 affects Calc pivot table recalculation, a core data analysis feature. libreoffice-0004 affects Writer table operations (merge, split, selection) on large tables. libreoffice-0005 affects Impress slide management in presentations with many slides.
The Fix
libreoffice-0001: Use unordered_map<size_t, size_t> to track previously-seen graphic details by hash, replacing the backward std::find() scan.
libreoffice-0002: Add unordered_set shadow of maItems in GroupFilter, populated in addMatchItem(), for O(1) membership test in match().
libreoffice-0003: Build unordered_set<SfxGroupId> before the slot registration loop for O(1) group dedup.
libreoffice-0004: Pass unordered_set<SwTableLine*> alongside the vector to InsertLine() for O(1) dedup.
libreoffice-0005: Build unordered_set<Paragraph*> from maSelectedParas before the paragraph loop for O(1) lookup.
Patch
Patches available in defects/libreoffice/patch/:
libreoffice-0001-wrtww8gr-graphic-dedup-quadratic.patchlibreoffice-0002-dpfilteredcache-groupfilter-match-linear.patchlibreoffice-0003-msgpool-group-dedup-quadratic.patchlibreoffice-0004-ndtbl1-insertline-dedup-quadratic.patchlibreoffice-0005-outlview-selectedparas-linear-scan.patch
Language: C++
What We Ask
- Confirm receipt and assign a Bugzilla reference (bugs.documentfoundation.org).
- Assess severity — libreoffice-0002 fires on every pivot table recalc; libreoffice-0001 fires on every WW8 export with duplicate graphics.
- Coordinate a disclosure date — we are targeting 90 days from first contact.
- We will credit the LibreOffice team in the public disclosure. Preferred acknowledgment format welcome.
Contact: see cover email. This brief is confidential until coordinated disclosure.