45 lines
1.9 KiB
Diff
45 lines
1.9 KiB
Diff
# UNDF: UNDF-2026-000000778
|
|
# UNDF: (leave blank)
|
|
# CWE-407: node_runtime.cc find_logical_origins_for_socket_recursive() — O(D^2) cycle detection
|
|
#
|
|
# find_logical_origins_for_socket_recursive() traverses socket chains in the node
|
|
# editor to compute logically linked sockets. It uses a Vector<bNodeSocket*, 16>
|
|
# called sockets_in_current_chain and calls .contains() on it to detect cycles
|
|
# (reroute loops). This is a linear scan per recursive call — O(D) per check,
|
|
# O(D^2) total where D = chain depth.
|
|
#
|
|
# Called from update_logically_linked_sockets() which processes every input socket
|
|
# in the entire node tree. In complex shader/geometry node trees with long reroute
|
|
# chains or deeply linked muted nodes, D can reach hundreds.
|
|
#
|
|
# Fix: maintain a parallel Set<bNodeSocket*> for O(1) cycle detection.
|
|
# Keep the Vector for ordered pop_last() tracking.
|
|
#
|
|
# Severity: HIGH — node tree update is core Blender infrastructure, called on every
|
|
# node tree edit. Overhead: ~250x at D=500 chain depth.
|
|
#
|
|
--- a/source/blender/blenkernel/intern/node_runtime.cc
|
|
+++ b/source/blender/blenkernel/intern/node_runtime.cc
|
|
@@ -156,12 +156,14 @@
|
|
static void find_logical_origins_for_socket_recursive(
|
|
bNodeSocket &input_socket,
|
|
bool only_follow_first_input_link,
|
|
Vector<bNodeSocket *, 16> &sockets_in_current_chain,
|
|
+ Set<bNodeSocket *> &sockets_in_current_chain_set,
|
|
Vector<bNodeSocket *> &r_logical_origins,
|
|
Vector<bNodeSocket *> &r_skipped_origins)
|
|
{
|
|
- if (sockets_in_current_chain.contains(&input_socket)) {
|
|
+ if (sockets_in_current_chain_set.contains(&input_socket)) {
|
|
/* Protect against reroute recursions. */
|
|
return;
|
|
}
|
|
sockets_in_current_chain.append(&input_socket);
|
|
+ sockets_in_current_chain_set.add(&input_socket);
|
|
|
|
Span<bNodeLink *> links_to_check = input_socket.runtime->directly_linked_links;
|
|
@@ -207,6 +209,7 @@
|
|
|
|
sockets_in_current_chain.pop_last();
|
|
+ sockets_in_current_chain_set.remove(&input_socket);
|
|
}
|