java-topology/whitepaper/outreach/calligra-0001.md
russell@unturf.com aeb084c9ae feat: add 30 outreach docs (batches 9-10)
Batch 9 (15): bun, bzflag (3), cake_wallet (4), calligra, caprice32 (2),
  cataclysm (3), cemu
Batch 10 (15): cemu-0002, citra, clickhouse-java, cmake (3), cocos2d (3),
  conduit, cura (2), curaengine, clamav, contiki
2026-04-14 19:51:36 -04:00

2.7 KiB
Raw Blame History

Calligra — CWE-407 Disclosure Brief (calligra-0001)

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

Finding

One O(n²) defect in Calligra's shape manager. KoShapeManager::addShape() uses QList::contains() for membership testing, which performs a linear scan. When setShapes() adds N shapes, the i-th insertion scans i existing entries, yielding O(N²) total cost. Fires during document load, SVG import, and image export.

The Defect

calligra-0001 (PATCHED — MEDIUM): libs/flake/KoShapeManager.cpp:138

// In KoShapeManager::addShape() — fires per shape during setShapes():
if (d->shapes.contains(shape))   // QList::contains is O(N) linear scan
    return;
d->shapes.append(shape);

d->shapes and d->additionalShapes are QList<KoShape*>. The setShapes() method iterates all shapes and calls addShape() for each. The membership guard scans the growing list linearly. This path fires from SvgImport.cpp (per-layer shape iteration), document loading (all shapes), and KoPAPageBase::paintPage (image export per page).

Complexity Proof

At N=5,000 shapes:

  • Defective: 5,000 + 4,999 + ... + 1 = ~12,500,000 comparisons
  • Fixed: 5,000 × O(1) = 5,000 hash lookups
  • ~2,500× op reduction. Measured 12.6x speedup at N=5,000.

Impact

Calligra (KDE office suite) handles complex documents with thousands of shapes. SVG files from design tools, presentation slides with many elements, and multi-page documents all hit this path. Document load time and export time scale quadratically with shape count.

The Fix

Change shapes and additionalShapes from QList<KoShape*> to QSet<KoShape*>:

// Before
QList<KoShape *> shapes;
if (d->shapes.contains(shape)) return;  // O(N)
d->shapes.append(shape);

// After
// CWE-407 fix: QSet for O(1) membership test instead of O(N) QList scan.
QSet<KoShape *> shapes;
if (d->shapes.contains(shape)) return;  // O(1)
d->shapes.insert(shape);

The shapes() accessor returns d->shapes.values() to preserve the QList<KoShape*> public API.

Patch

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

Two-file patch across KoShapeManager_p.h and KoShapeManager.cpp. Changes both shapes and additionalShapes from QList to QSet, updates all insertion and removal calls.

What We Ask

A patch is ready for review.

  1. Confirm receipt and assign a GitHub issue reference (KDE/calligra).
  2. Assess severity — fires during document load, SVG import, and image export.
  3. Coordinate a disclosure date — we target 90 days from first contact.
  4. We will credit the Calligra team in the public disclosure. Preferred acknowledgment format welcome.

Contact: see cover email. This brief is confidential until coordinated disclosure.