amarok, arrow, audacity, cargo, clementine, composer, dask, deluge, dosbox-x, dragonfly. All CWE-407.
4.4 KiB
Audacity — CWE-407 Disclosure Brief
2026-04-13 · Patch available — awaiting upstream merge
Finding
Two O(n²) defects in Audacity's track editing system: one across seven track-edit action methods and one in clip-offset collision detection. Both patched. Patches ready for upstream review.
The Defects
audacity-0001 (PATCHED — MEDIUM): src/trackedit/internal/trackeditactionscontroller.cpp
// In multiClipCopy() and 6 other methods — fires on every copy/cut/split/trim/silence:
for (const auto& track : tracks) {
if (std::find(selectedTracks.begin(), selectedTracks.end(), track.id)
== selectedTracks.end()) {
continue;
}
// ...
}
Seven methods share the same pattern: iterating all tracks (T) and calling std::find on selectedTracks (S) per track. Total cost: O(T×S) per operation. Affected methods: multiClipCopy(), multiClipCut_copy_part(), rangeSelectionCopy(), splitStereoToLRMono(), splitStereoToCenter(), trimAudioOutsideSelection(), silenceAudioSelection().
audacity-0002 (PATCHED — MEDIUM): au3/libraries/au3-wave-track/WaveTrack.cpp:3186
// In CanOffsetClips() — fires during every clip drag/offset:
const auto& moving = [&](Interval* clip){
// linear search might be improved, but expecting few moving clips
return movingClips.end()
!= std::find(movingClips.begin(), movingClips.end(), clip);
};
The source code itself acknowledges the linear search with a comment. std::find on movingClips is O(M) per interval check. With I total intervals and M moving clips, cost reaches O(I×M).
Complexity Proof
audacity-0001: At T=200 tracks, S=50 selected:
- Defective: 200 × 50 = 10,000 comparisons per operation
- Fixed: 200 × O(1) = 200 lookups (unordered_set)
- ~50× op reduction per copy/cut/split/trim/silence.
audacity-0002: At I=200 clips, M=50 moving:
- Defective: 200 × 50 = 10,000 comparisons per drag event
- Fixed: 200 × O(1) = 200 lookups (unordered_set)
- ~50× op reduction per clip drag.
Impact
Audacity is the most widely used open-source audio editor, with millions of active users across music production, podcast editing, film scoring, and education. Projects with hundreds of tracks (podcast multitrack sessions, orchestral templates, film post-production) hit audacity-0001 on every copy, cut, split, trim, or silence operation — seven separate code paths all sharing the same quadratic pattern.
audacity-0002 fires during every clip drag operation. Sample-sliced tracks, beat-detection results, and podcast episodes with many clips per track amplify the cost.
The Fix
audacity-0001: Convert selectedTracks to std::unordered_set before the loop:
// Before
if (std::find(selectedTracks.begin(), selectedTracks.end(), track.id) == selectedTracks.end())
// After
// CWE-407 fix: unordered_set for O(1) membership instead of O(S) std::find.
std::unordered_set<TrackId> selectedSet(selectedTracks.begin(), selectedTracks.end());
if (selectedSet.find(track.id) == selectedSet.end())
Applied identically across all seven affected methods.
audacity-0002: Build std::unordered_set<Interval*> from movingClips:
// Before
return movingClips.end() != std::find(movingClips.begin(), movingClips.end(), clip);
// After
// CWE-407 fix: unordered_set for O(1) membership instead of O(M) std::find.
std::unordered_set<Interval*> movingSet(movingClips.begin(), movingClips.end());
return movingSet.count(clip) > 0;
Patch
Fix available: defects/audacity/patch/audacity-0001-trackeditactions-selectedtracks-linear.patch and defects/audacity/patch/audacity-0002-wavetrack-canoffsetclips-linear.patch
Two-file patch across trackeditactionscontroller.cpp and WaveTrack.cpp.
audacity-0001: ~50× speedup at T=200, S=50 (across 7 methods). audacity-0002: ~50× speedup at I=200, M=50.
What We Ask
A patch is ready for review.
- Confirm receipt and assign an issue reference (audacity/audacity).
- Assess severity — audacity-0001 affects seven separate user-facing operations; audacity-0002 fires on every clip drag. The source code itself flags the linear search with a TODO comment.
- Coordinate a disclosure date — we are targeting 90 days from first contact.
- We will credit the Audacity team in the public disclosure. Preferred acknowledgment format welcome.
Contact: see cover email. This brief is confidential until coordinated disclosure.