natron/ardour: 2 CWE-407 defects, all 5 MOADs scanned
natron-0001: Node graph traversal visited-set O(N^2) via std::list+std::find Engine/Node.cpp computeHashRecursive and 3+ sibling functions use std::list<Node*> as visited set with O(N) std::find per visit = O(N^2). Fix: std::unordered_set<Node*>. 249.5x at N=500 nodes. 3/3 PASS. ardour-0001: PluginManager blacklist/rescan PluginInfoList O(I*N) libs/ardour/plugin_manager.cc blacklist() and rescan_plugin() call std::find on pil (N plugins) for each of I scan-log entries = O(I*N). Fix: unordered_set + remove_if. 19.4x at N=1000 I=20. 3/3 PASS. MOADs 0002-0005: CLEAN with notes in SCAN-MOAD-0002-0005.md each.
This commit is contained in:
parent
89de6df1d4
commit
e292f57db2
8 changed files with 506 additions and 0 deletions
125
defects/ardour/unit/ArdourTest.java
Normal file
125
defects/ardour/unit/ArdourTest.java
Normal file
|
|
@ -0,0 +1,125 @@
|
|||
import java.util.*;
|
||||
|
||||
/**
|
||||
* CWE-407 simulation tests for Ardour defects.
|
||||
*
|
||||
* ardour-0001: PluginManager blacklist/rescan PluginInfoList O(I * N)
|
||||
*
|
||||
* PluginManager::blacklist() and ::rescan_plugin() in plugin_manager.cc
|
||||
* iterate over I entries from the scan log for the path being processed and
|
||||
* call std::find on pil (the master PluginInfoList with N entries) for each.
|
||||
* std::find on std::list<PluginInfoPtr> is O(N) per call, giving O(I * N) total.
|
||||
*
|
||||
* In a studio with 500 VST3 plugins, blacklisting a bundle with I=10 entries
|
||||
* costs 5000 pointer comparisons instead of 510 (O(N+I) for the fixed version).
|
||||
*
|
||||
* Fix: build an unordered_set of the I entries to remove, then do one
|
||||
* remove_if pass over pil — O(N + I) total.
|
||||
*
|
||||
* Both blacklist() and rescan_plugin() share the same pattern and both
|
||||
* need the same fix.
|
||||
*/
|
||||
public class ArdourTest {
|
||||
|
||||
// ---------------------------------------------------------------
|
||||
// ardour-0001: PluginInfoList std::find inside scan-log loop
|
||||
// ---------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Simulate defective removal: for each of I entries to remove,
|
||||
* std::find scans all N entries in pil = O(I * N).
|
||||
*
|
||||
* @param pil total number of plugins in master PluginInfoList
|
||||
* @param toRemove number of plugins to remove from this scan log entry
|
||||
* @return total comparison operations
|
||||
*/
|
||||
static long blacklistDefective(int pil, int toRemove) {
|
||||
long ops = 0;
|
||||
// Simulate worst case: each to-remove entry is found at end of pil
|
||||
for (int i = 0; i < toRemove; i++) {
|
||||
// std::find scans from begin() to the found element (worst case: N)
|
||||
for (int k = 0; k < pil; k++) {
|
||||
ops++;
|
||||
}
|
||||
pil--; // list shrinks after erase
|
||||
}
|
||||
return ops;
|
||||
}
|
||||
|
||||
/**
|
||||
* Simulate fixed removal: build unordered_set of I entries (O(I)),
|
||||
* then single remove_if pass over pil (O(N)) = O(N + I) total.
|
||||
*
|
||||
* @param pil total number of plugins in master PluginInfoList
|
||||
* @param toRemove number of plugins to remove from this scan log entry
|
||||
* @return total comparison operations
|
||||
*/
|
||||
static long blacklistFixed(int pil, int toRemove) {
|
||||
long ops = 0;
|
||||
// Build unordered_set: O(I) inserts
|
||||
ops += toRemove;
|
||||
// remove_if pass: O(N) hash lookups (O(1) each)
|
||||
ops += pil;
|
||||
return ops;
|
||||
}
|
||||
|
||||
static void testBlacklistSmallBundle() {
|
||||
// Typical: 500 VST3 plugins, blacklisting a bundle with 5 variants
|
||||
int N = 500;
|
||||
int I = 5;
|
||||
|
||||
long defectOps = blacklistDefective(N, I);
|
||||
long fixedOps = blacklistFixed(N, I);
|
||||
double ratio = (double) defectOps / fixedOps;
|
||||
|
||||
System.out.printf("ardour-0001 blacklist small bundle (N=%d plugins, I=%d entries):%n", N, I);
|
||||
System.out.printf(" defect_ops=%d fixed_ops=%d ratio=%.1fx%n",
|
||||
defectOps, fixedOps, ratio);
|
||||
assert ratio > 2.0 : "Expected overhead, got " + ratio;
|
||||
System.out.println(" PASS");
|
||||
}
|
||||
|
||||
static void testBlacklistLargeScan() {
|
||||
// Stress case: 1000 plugins, rescan of bundle with 20 entries
|
||||
int N = 1000;
|
||||
int I = 20;
|
||||
|
||||
long defectOps = blacklistDefective(N, I);
|
||||
long fixedOps = blacklistFixed(N, I);
|
||||
double ratio = (double) defectOps / fixedOps;
|
||||
|
||||
System.out.printf("ardour-0001 blacklist large scan (N=%d plugins, I=%d entries):%n", N, I);
|
||||
System.out.printf(" defect_ops=%d fixed_ops=%d ratio=%.1fx%n",
|
||||
defectOps, fixedOps, ratio);
|
||||
assert ratio > 5.0 : "Expected significant overhead, got " + ratio;
|
||||
System.out.println(" PASS");
|
||||
}
|
||||
|
||||
static void testRescanPlugin() {
|
||||
// rescan_plugin() same pattern as blacklist()
|
||||
// 800 LADSPA plugins, rescan with I=15 entries
|
||||
int N = 800;
|
||||
int I = 15;
|
||||
|
||||
long defectOps = blacklistDefective(N, I);
|
||||
long fixedOps = blacklistFixed(N, I);
|
||||
double ratio = (double) defectOps / fixedOps;
|
||||
|
||||
System.out.printf("ardour-0001 rescan_plugin (N=%d plugins, I=%d entries):%n", N, I);
|
||||
System.out.printf(" defect_ops=%d fixed_ops=%d ratio=%.1fx%n",
|
||||
defectOps, fixedOps, ratio);
|
||||
assert ratio > 5.0 : "Expected significant overhead, got " + ratio;
|
||||
System.out.println(" PASS");
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------
|
||||
// Main
|
||||
// ---------------------------------------------------------------
|
||||
|
||||
public static void main(String[] args) {
|
||||
testBlacklistSmallBundle();
|
||||
testBlacklistLargeScan();
|
||||
testRescanPlugin();
|
||||
System.out.println("\nAll Ardour CWE-407 tests PASS");
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue