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).
38 lines
1.6 KiB
Markdown
38 lines
1.6 KiB
Markdown
# Kotlin Compiler — Diamond Recursion CWE-407 Scan: CLEAN
|
||
|
||
**Pattern:** Recursive cycle/reachability without visited set (O(2^D) on diamond DAGs)
|
||
**Scan date:** 2026-03-29
|
||
**Scope:** `compiler/frontend/src/`, `compiler/`
|
||
|
||
## Method
|
||
|
||
Searched for `isCyclic`, `hasCycle`, `createsCycle`, `isReachable`, `canReach`,
|
||
`hasPath`, `detectCycle` across all Kotlin and Java sources. Examined each
|
||
recursive function for missing visited-accumulator parameters.
|
||
|
||
## Key candidates reviewed
|
||
|
||
### `findLoopsInSupertypes.kt — isReachable()`
|
||
`compiler/frontend/src/.../resolve/findLoopsInSupertypes.kt` line 56.
|
||
|
||
Called in a loop (line 40) for each supertype. Each call to `isReachable()` creates
|
||
a fresh `DFS.VisitedWithSet()` and performs a full DFS. This is O(S × (V+E)) where
|
||
S = supertype count. Not diamond recursion — each call is independent and properly
|
||
visits nodes. CLEAN.
|
||
|
||
### `CyclicAnnotationsChecker`
|
||
`compiler/frontend/src/.../resolve/checkers/CyclicAnnotationsChecker.kt`.
|
||
|
||
`Checker` class maintains instance-level `visitedAnnotationDescriptors` mutableSetOf.
|
||
`typeHasCycle()` checks `visitedAnnotationDescriptors.add()` before recursing. CLEAN.
|
||
|
||
### `NonExpansiveInheritanceRestrictionChecker.kt`
|
||
Already addressed by `kotlin-0001-collectreachable-hashset.patch` — the `collectReachable`
|
||
function returns a `HashSet` for O(1) membership. Not the diamond recursion pattern.
|
||
|
||
## Verdict
|
||
|
||
CLEAN for diamond recursion pattern. Kotlin compiler uses `DFS.VisitedWithSet()`
|
||
for all DFS traversal, mutable visited sets for annotation cycle detection,
|
||
and existing patches address the unrelated O(N²) List-contains issues.
|
||
No unprotected recursive DAG traversal found.
|