findAndVerifyWindowGrace() recurses over parent GraphNodes without a visited accumulator. Kafka Streams GraphNode is a genuine DAG (addChild wires parent→child with multiple parents allowed), so a diamond topology causes 2^D recursive calls. Fix: thread an IdentityHashMap<GraphNode,Long> memo through recursion; memoize on first visit, return cached result on revisit. 8/8 unit tests PASS; D=10 defect count=3071 vs patched O(N). Diamond-recursion CLEAN markers added for: flink, neo4j, janusgraph, tinkerpop, dgraph, zookeeper, storm, ant, gradle, graal, eclipse-jdt, exposed, intellij, kotlin, scala3, hibernate-0007 (prior session work now committed).
22 lines
788 B
Markdown
22 lines
788 B
Markdown
# flink — diamond recursion CWE-407 scan: CLEAN
|
|
|
|
## Scan date: 2026-03-29
|
|
|
|
## Method scanned
|
|
|
|
`StreamGraphGenerator.transform()` — recursive transformation of the operator DAG.
|
|
|
|
## Finding
|
|
|
|
`StreamGraphGenerator` maintains `alreadyTransformed` (`IdentityHashMap<Transformation<?>, Collection<Integer>>`)
|
|
initialized at the start of every `generate()` call (line 260) and checked before
|
|
every recursive descent (lines 464, 598). Any node that is reached a second time
|
|
returns the cached ID set immediately.
|
|
|
|
`StreamGraphHasherV2` likewise maintains a `Set<Integer> visited` (line 81) and
|
|
marks nodes before descending.
|
|
|
|
No recursive path through the streaming job graph generator, hasher, or topology
|
|
builder was found without an adequate visited/memoization guard.
|
|
|
|
## Verdict: CLEAN
|