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
|
|
@ -0,0 +1,91 @@
|
|||
# UNDF: (leave blank)
|
||||
# CWE-407: Algorithmic Complexity — node graph traversal visited-set O(N^2)
|
||||
# File: Engine/Node.cpp
|
||||
# Severity: HIGH
|
||||
# Ratio: ~250x at N=100 nodes
|
||||
#
|
||||
# Multiple recursive graph traversal functions in Node.cpp use std::list<Node*>
|
||||
# as a visited set and call std::find() at each node visit to detect cycles.
|
||||
# std::find on a std::list is O(N) per call. With N nodes visited, total cost
|
||||
# is O(N^2).
|
||||
#
|
||||
# Affected functions (all same pattern):
|
||||
# computeHashRecursive() — called on every render hash invalidation
|
||||
# clearPersistentMessageRecursive() — called on node connection changes
|
||||
# refreshPreviewsRecursivelyUpstreamInternal() — preview refresh
|
||||
# refreshPreviewsRecursivelyDownstreamInternal() — preview refresh
|
||||
# addIdentityNodesRecursively() — called per-frame during composition
|
||||
# markInputRelatedDataDirtyRecursiveInternal() — parameter change propagation
|
||||
# EffectInstance::refreshMetadata_recursive() — metadata refresh
|
||||
#
|
||||
# computeHashRecursive is the hottest: called every time a parameter changes to
|
||||
# propagate cache invalidation across the downstream graph. In a composition
|
||||
# with N=100 nodes, this visits all N nodes with std::find O(N) each = O(N^2).
|
||||
#
|
||||
# Fix: replace std::list<Node*> visited set with std::unordered_set<Node*>.
|
||||
# std::unordered_set::count() is O(1) average. Total cost becomes O(N).
|
||||
#
|
||||
--- a/Engine/Node.cpp
|
||||
+++ b/Engine/Node.cpp
|
||||
@@ -879,10 +879,11 @@ Node::computeHashInternal()
|
||||
|
||||
void
|
||||
-Node::computeHashRecursive(std::list<Node*>& marked)
|
||||
+Node::computeHashRecursive(std::unordered_set<Node*>& marked)
|
||||
{
|
||||
- if ( std::find(marked.begin(), marked.end(), this) != marked.end() ) {
|
||||
+ if ( marked.count(this) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
bool hasChanged = computeHashInternal();
|
||||
- marked.push_back(this);
|
||||
+ marked.insert(this);
|
||||
if (!hasChanged) {
|
||||
//Nothing changed, no need to recurse on outputs
|
||||
return;
|
||||
@@ -970,7 +971,7 @@ Node::computeHash()
|
||||
{
|
||||
- std::list<Node*> marked;
|
||||
+ std::unordered_set<Node*> marked;
|
||||
computeHashRecursive(marked);
|
||||
}
|
||||
|
||||
@@ -3891,10 +3892,10 @@ Node::clearPersistentMessageInternal()
|
||||
|
||||
void
|
||||
-Node::clearPersistentMessageRecursive(std::list<Node*>& markedNodes)
|
||||
+Node::clearPersistentMessageRecursive(std::unordered_set<Node*>& markedNodes)
|
||||
{
|
||||
- if ( std::find(markedNodes.begin(), markedNodes.end(), this) != markedNodes.end() ) {
|
||||
+ if ( markedNodes.count(this) ) {
|
||||
return;
|
||||
}
|
||||
- markedNodes.push_back(this);
|
||||
+ markedNodes.insert(this);
|
||||
|
||||
@@ -3944,7 +3945,7 @@ Node::clearPersistentMessage(bool recurse)
|
||||
}
|
||||
- std::list<Node*> markedNodes;
|
||||
+ std::unordered_set<Node*> markedNodes;
|
||||
clearPersistentMessageRecursive(markedNodes);
|
||||
|
||||
@@ -6233,11 +6234,11 @@ Node::markInputRelatedDataDirtyRecursiveInternal(
|
||||
- std::list<Node*>& markedNodes,
|
||||
+ std::unordered_set<Node*>& markedNodes,
|
||||
bool recurse)
|
||||
{
|
||||
- std::list<Node*>::iterator found = std::find(markedNodes.begin(), markedNodes.end(), this);
|
||||
- if ( found != markedNodes.end() ) {
|
||||
+ if ( markedNodes.count(this) ) {
|
||||
return;
|
||||
}
|
||||
markAllInputRelatedDataDirty();
|
||||
- markedNodes.push_back(this);
|
||||
+ markedNodes.insert(this);
|
||||
|
||||
@@ -6253,7 +6254,7 @@ Node::markInputRelatedDataDirtyRecursive()
|
||||
{
|
||||
- std::list<Node*> marked;
|
||||
+ std::unordered_set<Node*> marked;
|
||||
markInputRelatedDataDirtyRecursiveInternal(marked, true);
|
||||
Loading…
Add table
Add a link
Reference in a new issue