java-topology/whitepaper/outreach/libreoffice-0001.md
russell@unturf.com 652608142a feat: close outreach doc gap — 276 docs (batches 11-16)
All projects with patches now have outreach docs. 276 new docs covering
CWE-407, CWE-312, CWE-362 across C, C++, Java, Python, Go, Rust, C#,
PHP, Ruby, JavaScript, Dart, Erlang, R, and more.

Outreach gap: 276 -> 0.
2026-04-15 13:57:42 -04:00

2.7 KiB
Raw Blame History

LibreOffice — CWE-407 Disclosure Brief (libreoffice-0001)

2026-04-13 · Patch available — awaiting upstream merge

Finding

One O(MC) defect in LibreOffice Calc's pivot table XML export. The SavePivotTableXml() method in xepivotxml.cxx uses std::find on a vector of cache field items to locate the index of each member name, producing O(MC) total cost where M = members and C = cache items.

The Defect

libreoffice-0001 (PATCHED — HIGH): sc/source/filter/excel/xepivotxml.cxx:1404

for (const auto & rMember : aMembers)
{
    auto it = std::find(aCacheFieldItems.begin(), aCacheFieldItems.end(),
                        rMember.maName);  // O(C) per member
    if (it != aCacheFieldItems.end())
    {
        size_t nCachePos = std::distance(aCacheFieldItems.begin(), it);
        // ...
    }
}

For each member M in the pivot field, std::find scans the entire aCacheFieldItems vector (O(C)) to locate the matching cache index. With a text dimension containing thousands of distinct values, both M and C grow large.

Complexity Proof

At M=5,000 members, C=5,000 cache items:

  • Defective: 5,000 × 2,500 (avg) = 12,500,000 comparisons
  • Fixed: 5,000 (map build) + 5,000 O(1) lookups = 10,000 operations
  • ~1,250× op reduction.

Impact

LibreOffice is the most widely used open-source office suite. Pivot tables with text dimensions (product names, customer IDs, city names) commonly contain thousands of distinct values. Saving such spreadsheets to XLSX format triggers the quadratic path for each pivot table dimension.

The Fix

Build a std::unordered_map<OUString, size_t> from cache items before the member loop:

// After
std::unordered_map<OUString, size_t> aCacheItemIndex;
aCacheItemIndex.reserve(aCacheFieldItems.size());
for (size_t k = 0; k < aCacheFieldItems.size(); ++k)
    aCacheItemIndex.emplace(aCacheFieldItems[k], k);

for (const auto & rMember : aMembers) {
    auto mapIt = aCacheItemIndex.find(rMember.maName);
    if (mapIt != aCacheItemIndex.end()) {
        size_t nCachePos = mapIt->second;  // O(1)
    }
}

Patch

Fix available: defects/libreoffice-0001/patch/libreoffice-0001.patch

Single-file patch in sc/source/filter/excel/xepivotxml.cxx. ~1,250× speedup at 5,000 cache items.

What We Ask

A patch is ready for review.

  1. Confirm receipt and assign a GitHub issue reference (LibreOffice Bugzilla).
  2. Assess severity — fires on every pivot table save to XLSX, quadratic in dimension cardinality.
  3. Coordinate a disclosure date — we are targeting 90 days from first contact.
  4. 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.