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.
125 lines
4.7 KiB
Java
125 lines
4.7 KiB
Java
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");
|
|
}
|
|
}
|