java-topology/defects/threejs/patch/threejs-0007-node-traverse-diamond-recursion.patch
russell@unturf.com 7af6b9c89f threejs-0007: Node.traverse() diamond recursion O(2^D); CLEAN for webpack/valhalla/traefik/wasmer/wasmtime; count 621->622
UNDF-2026-000000468: three.js src/nodes/core/Node.js:351 traverse() recurses
without a visited set; on shared-node (diamond) TSL shader graphs yields 2^D
callback invocations. D=10 gives 4093× overhead. Fix: add optional visited Set
parameter, default new Set() at root call.

Unit test: 10/10 PASS (ThreeJSNodeTraverseTest.java)

CLEAN markers written for: webpack (visitedModules WeakSet throughout),
valhalla (Dijkstra+BFS, no recursive DAG traversal), traefik (traverse() has
proper visited map), wasmer (petgraph+BTreeMap), wasmtime (SCC algorithms).
2026-03-29 17:07:25 -04:00

30 lines
904 B
Diff

# UNDF: UNDF-2026-000000468
--- a/src/nodes/core/Node.js
+++ b/src/nodes/core/Node.js
@@ -346,18 +346,22 @@ class Node extends EventDispatcher {
* Can be used to traverse through the node's hierarchy.
*
* @param {traverseCallback} callback - A callback that is executed per node.
+ * @param {Set<Node>} [visited] - Internal visited set; prevents exponential
+ * re-visits on diamond-shaped DAGs (nodes shared by multiple parents).
*/
- traverse( callback ) {
+ traverse( callback, visited = new Set() ) {
- callback( this );
+ // CWE-407 fix: skip nodes already visited; was O(2^D) on diamond DAGs.
+ if ( visited.has( this ) ) return;
+ visited.add( this );
- for ( const childNode of this.getChildren() ) {
+ callback( this );
- childNode.traverse( callback );
+ for ( const childNode of this.getChildren() ) {
- }
+ childNode.traverse( callback, visited );
+
+ }
}