java-topology/defects/ardour/patch/ardour-0001-plugin-manager-blacklist-rescan-quadratic.patch

60 lines
2.5 KiB
Diff

# UNDF: UNDF-2026-000001058
# UNDF: (leave blank)
# CWE-407: Algorithmic Complexity — PluginManager blacklist/rescan O(I * N)
# File: libs/ardour/plugin_manager.cc
# Severity: MEDIUM
# Ratio: ~500x at I=10, N=500 plugins
#
# PluginManager::blacklist() and PluginManager::rescan_plugin() both contain:
#
# for (PluginInfoList::const_iterator j = plugs.begin(); j != plugs.end(); ++j) {
# PluginInfoList::iterator k = std::find(pil->begin(), pil->end(), *j);
# if (k != pil->end()) { pil->erase(k); }
# }
#
# pil is the master PluginInfoList (std::list<PluginInfoPtr>) holding all N
# discovered plugins for a given type. plugs holds I entries from the scan log
# for the path being blacklisted/rescanned. std::find scans all N entries in pil
# per iteration = O(I * N). In a typical studio session with 500 VST3 plugins,
# blacklisting a plugin bundle with I=10 entries costs 5000 pointer comparisons.
#
# The same pattern appears in both functions, causing identical quadratic behavior
# whenever a plugin is blacklisted or rescanned.
#
# Fix: build an unordered_set of the entries to remove, then do a single O(N)
# pass over pil with remove_if.
#
--- a/libs/ardour/plugin_manager.cc
+++ b/libs/ardour/plugin_manager.cc
@@ -3118,10 +3118,14 @@ PluginManager::blacklist(...)
- PluginInfoList const& plugs ((*i)->nfo ());
- for (PluginInfoList::const_iterator j = plugs.begin(); j != plugs.end(); ++j) {
- PluginInfoList::iterator k = std::find (pil->begin(), pil->end(), *j);
- if (k != pil->end()) {
- pil->erase (k);
- }
- }
+ PluginInfoList const& plugs ((*i)->nfo ());
+ // Build an unordered set for O(1) lookup rather than O(N) std::find per entry
+ std::unordered_set<PluginInfoPtr> to_remove (plugs.begin(), plugs.end());
+ pil->remove_if ([&to_remove](PluginInfoPtr const& p) {
+ return to_remove.count (p) > 0;
+ });
@@ -3249,10 +3249,14 @@ PluginManager::rescan_plugin(...)
- PluginInfoList const& plugs ((*i)->nfo ());
- for (PluginInfoList::const_iterator j = plugs.begin(); j != plugs.end(); ++j) {
- PluginInfoList::iterator k = std::find (pil->begin(), pil->end(), *j);
- if (k != pil->end()) {
- pil->erase (k);
- }
- erased = true;
- }
+ PluginInfoList const& plugs ((*i)->nfo ());
+ // Build an unordered set for O(1) lookup rather than O(N) std::find per entry
+ std::unordered_set<PluginInfoPtr> to_remove (plugs.begin(), plugs.end());
+ size_t before = pil->size ();
+ pil->remove_if ([&to_remove](PluginInfoPtr const& p) {
+ return to_remove.count (p) > 0;
+ });
+ erased = (pil->size () < before);