Kotlin: confirmed CLEAN (DFS.VisitedWithSet throughout, existing marker valid) Scala3: confirmed CLEAN (BaseDataBuilder.addAll deduplicates, existing marker valid) GHC: CLEAN — NameSet/UniqSet/ExpansionFuel tracking in all superclass expansion paths (checkClassCycles, mk_strict_superclasses, transSuperClasses, closeWrtFunDeps) Groovy: CLEAN for diamond — getAllInterfaces/collectAllInterfacesReverseOrder/addAllInterfaces all use if(set.add(node)) guards (GROOVY-11036) groovy-0001/0002 patch docs created (O(N²) membership scans, pre-existing UNDF assigned) Clojure: CLEAN (set-based BFS, supers/ancestors use set-based worklist)
50 lines
2.1 KiB
Markdown
50 lines
2.1 KiB
Markdown
# Groovy Compiler — Diamond Recursion CWE-407 Scan: CLEAN
|
||
|
||
**Pattern:** Recursive O(2^D) traversal of interface/trait hierarchy without visited set
|
||
**Scan date:** 2026-03-29
|
||
**Scope:** `src/main/java/org/codehaus/groovy/`
|
||
|
||
## Method
|
||
|
||
Searched for recursive ClassNode hierarchy traversal without proper `if (set.add())` guards.
|
||
Traced all callers of `getInterfaces()`, `getSuperClass()`, `getAllInterfaces()`,
|
||
`getInterfacesAndSuperInterfaces()`, and `collectAllInterfacesReverseOrder()`.
|
||
|
||
## Key candidates reviewed
|
||
|
||
### `ClassNode.getAllInterfaces()` — private `getAllInterfaces(Set)`
|
||
`src/main/java/org/codehaus/groovy/ast/ClassNode.java` lines 441–452.
|
||
|
||
Fixed in GROOVY-11036: now uses `if (set.add(face)) face.getAllInterfaces(set)`.
|
||
The guard on `set.add()` return value prevents re-traversal of shared ancestors.
|
||
CLEAN (post-fix).
|
||
|
||
### `GeneralUtils.addAllInterfaces()` / `getInterfacesAndSuperInterfaces()`
|
||
`src/main/java/org/codehaus/groovy/ast/tools/GeneralUtils.java` lines 491–518.
|
||
|
||
Uses `if (result.add(in)) { addAllInterfaces(result, in); }` for interface branches.
|
||
Superclass branch is linear (Java single-inheritance), so no diamond possible there.
|
||
CLEAN.
|
||
|
||
### `Traits.collectAllInterfacesReverseOrder()`
|
||
`src/main/java/org/codehaus/groovy/transform/trait/Traits.java` lines 307–316.
|
||
|
||
Uses `if (interfaces.add(iNode)) collectAllInterfacesReverseOrder(iNode, interfaces)`.
|
||
Correct guard. CLEAN.
|
||
|
||
### `ResolveVisitor` cycle detection
|
||
`src/main/java/org/codehaus/groovy/control/ResolveVisitor.java` lines 1363–1381.
|
||
|
||
Uses BFS with `done.add(next)` as the loop guard. CLEAN.
|
||
|
||
### `WideningCategories.lowestUpperBound()`
|
||
`src/main/java/org/codehaus/groovy/ast/tools/WideningCategories.java` lines 319–447.
|
||
|
||
Recursion on superclass chain (linear, no diamond). Interface sets pre-computed via
|
||
`getInterfacesAndSuperInterfaces()` which is guarded. CLEAN.
|
||
|
||
## Verdict
|
||
|
||
CLEAN for O(2^D) diamond recursion. Groovy uses `if (set.add(node))` guards on all
|
||
recursive interface hierarchy traversal. The two existing defects (groovy-0001,
|
||
groovy-0002) are O(N²) list-membership scans, not diamond recursion.
|