java-topology/whitepaper/outreach/kdenlive.md
russell@unturf.com c24246e2e2 feat: add 5 outreach docs (33 defects) + mastodon CWE-1333 benchmark
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
2026-04-13 14:03:16 -04:00

7 KiB

Kdenlive — CWE-407 Disclosure Brief

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

Finding

Ten CWE-407 algorithmic complexity defects in Kdenlive across timeline operations, thumbnail caching, preview chunk management, asset parameter lookups, keyframe consistency checking, and guide navigation. All patched. One additional CWE-362 data race (MOAD-0005) also patched. All patches ready for upstream review.

The Defects

kdenlive-0001 (PATCHED — MEDIUM): src/utils/thumbnailcache.hpp + thumbnailcache.cpp

// m_storedOnDisk / m_storedVolatile: vector<int> with std::find for dedup
std::unordered_map<QString, std::vector<int>> m_storedVolatile;
mutable std::unordered_map<QString, std::vector<int>> m_storedOnDisk;
// std::find() is O(V) per lookup — fires in getThumbnail, storeThumbnail, saveCachedThumbs

m_storedOnDisk and m_storedVolatile use vector<int> with std::find() for membership checks. O(C*P*V) in saveCachedThumbs, O(F*V) in invalidateThumbsForClip.

kdenlive-0002 (PATCHED — MEDIUM): src/timeline2/model/timelinemodel.cpp — requestClipsMixing

// clipIds vector<int> scanned with std::find inside per-clip loop
if (std::find(clipIds.begin(), clipIds.end(), previousClip) != clipIds.end() && ...)

Multiple std::find() calls per iteration over selected clips. O(N^2) where N = selected clips.

kdenlive-0003 (PATCHED — MEDIUM): src/timeline2/view/timelinecontroller.cpp — moveGroup

// sorted_clips vector<int> scanned with std::find inside per-clip loop
if (std::find(sorted_clips.begin(), sorted_clips.end(), mixData.first.firstClipId) == ...)

O(N^2) where N = grouped clips being moved.

kdenlive-0004 (PATCHED — MEDIUM): src/timeline2/model/timelinemodel.cpp — requestClipResizeAndTimeWarp

// all_items std::list<int> with std::find inside loop over currentSelection
if (id == itemId || std::find(all_items.begin(), all_items.end(), id) != all_items.end() || ...)

O(N^2) where N = current selection size.

kdenlive-0005 (PATCHED — MEDIUM): src/timeline2/view/previewmanager.h + previewmanager.cpp

// m_renderedChunks/m_dirtyChunks QVariantList with .contains() in loops
if (!m_renderedChunks.contains(frame) && !m_dirtyChunks.contains(frame))

O(D*M) chunk dedup in reloadChunks, O(N*(R+D)) in invalidatePreview/addPreviewRange/gotChunks.

kdenlive-0006 (PATCHED — MEDIUM): src/timeline2/view/timelinecontroller.cpp — gotoNextGuide/gotoPreviousGuide

// std::find on canceled vector in loop over guides
if (std::find(canceled.begin(), canceled.end(), guidePos) != canceled.end())

O(G*C) where G = guides, C = canceled/ignored guide positions.

kdenlive-0007 (PATCHED — MEDIUM): src/assets/model/assetparametermodel.hpp + assetparametermodel.cpp

// m_rows QVector<QString> with indexOf() called inside loops over m_params/m_fixedParams
QModelIndex ix = index(m_rows.indexOf(param.first), 0);

O(P*R) in getAllParameters, toJson, valueAsJson, setParameters.

kdenlive-0008 (PATCHED — MEDIUM): src/assets/view/widgets/urllistparamwidget.cpp — addItemsInSameFolder

// std::find iterating QMap values in loop over directory entries
if (std::find((*listValues).cbegin(), (*listValues).cend(), path) == (*listValues).cend())

O(E*M) where E = directory entries, M = existing map values.

kdenlive-0009 (PATCHED — HIGH, MOAD-0005/CWE-362): src/core.cpp + src/mainwindow.h

Data race on QMap<QString, QImage> m_lumacache. buildLumaThumbs() runs via QtConcurrent::run() on a worker thread, reading and writing the static QMap without any mutex, while UI widget code reads/writes the same map from the main thread. QMap offers no thread safety for concurrent writes. Fix: add QMutex to protect all m_lumacache accesses.

kdenlive-0010 (PATCHED — LOW-MEDIUM): src/assets/keyframes/model/keyframemodellist.cpp — checkConsistency

// QList<GenTime>::contains() O(K) called inside loops — O(P * K^2)
if (!fullList.contains(time)) { fullList << time; }
// ... then:
if (!list.contains(time)) { ... }

Phase 1 builds a union list, phase 2 verifies consistency. Both use O(K) QList::contains() per element. At K=500 keyframes, P=3 parameters: ~750,000 comparisons. Fix: std::set<GenTime> for O(log K) lookup. 250x speedup at K=500.

Complexity Proof

kdenlive-0002/0003/0004: At N=500 selected clips:

  • Defective: ~125,000 comparisons per operation
  • Fixed: ~500 hash lookups
  • 250x op reduction

kdenlive-0010: At K=500, P=3:

  • Defective: 750,000 comparisons
  • Fixed: ~4,500 set operations (log2(500) ~ 9)
  • 166x op reduction

Impact

Kdenlive is a major open-source non-linear video editor used by content creators, educators, and professional video editors worldwide. Most defects fire during interactive timeline operations (clip selection, group moves, resizing, mixing), making them user-facing in real-time editing sessions. kdenlive-0001 fires during thumbnail cache management, affecting project load and scrubbing. kdenlive-0010 fires at clip load time for keyframed effects.

The Fix

kdenlive-0001: Replace vector<int> with unordered_set<int> for m_storedOnDisk and m_storedVolatile.

kdenlive-0002/0003/0004/0006: Build unordered_set<int> from the vector/list before the loop for O(1) membership.

kdenlive-0005: Add shadow QSet<int> alongside QVariantList for O(1) .contains().

kdenlive-0007: Add QHash<QString,int> m_rowIndex shadow map for O(1) name-to-row lookup.

kdenlive-0008: Build QSet<QString> of existing values before the directory scan loop.

kdenlive-0009: Add QMutex m_lumacacheMutex and QMutexLocker around all m_lumacache accesses.

kdenlive-0010: Use std::set<GenTime> for O(log K) dedup in both phases.

Patch

Patches available in defects/kdenlive/patch/:

  • kdenlive-0001-thumbnailcache-storedOnDisk-linear-find.patch
  • kdenlive-0002-timelinemodel-clipIds-mix-linear-find.patch
  • kdenlive-0003-timelinecontroller-sorted-clips-linear-find.patch
  • kdenlive-0004-timelinemodel-resize-all-items-linear-find.patch
  • kdenlive-0005-previewmanager-chunk-lists-linear-contains.patch
  • kdenlive-0006-timelinecontroller-canceled-guides-linear-find.patch
  • kdenlive-0007-assetparametermodel-rows-indexOf-in-loops.patch
  • kdenlive-0008-urllistparamwidget-addItemsInSameFolder-linear-find.patch
  • kdenlive-0009-lumacache-qtconcurrent-race.patch
  • kdenlive-0010-keyframemodellist-checkconsistency-qlists-contains.patch

Language: C++

What We Ask

  1. Confirm receipt and assign a KDE Bugzilla reference (or invent.kde.org issue).
  2. Assess severity — kdenlive-0009 is a data race (undefined behavior); kdenlive-0002/0003/0004 fire on every multi-clip edit.
  3. Coordinate a disclosure date — we are targeting 90 days from first contact.
  4. We will credit the Kdenlive team in the public disclosure. Preferred acknowledgment format welcome.

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