quarkus-0003: BeanDeployment.recursiveBuild transitive bindings diamond O(2^D); count 638→639

This commit is contained in:
russell@unturf.com 2026-03-29 17:14:17 -04:00
parent 7af6b9c89f
commit 29308bfe00
4 changed files with 89 additions and 1 deletions

View file

@ -579,5 +579,6 @@
"typescript-0003": "UNDF-2026-000000441",
"kafka-0009": "UNDF-2026-000000451",
"hibernate-0007": "UNDF-2026-000000464",
"threejs-0007": "UNDF-2026-000000468"
"threejs-0007": "UNDF-2026-000000468",
"quarkus-0003": "UNDF-2026-000000469"
}

View file

@ -1,3 +1,4 @@
# UNDF: UNDF-2026-000000352
## Classification
| Field | Value |

View file

@ -0,0 +1,85 @@
# UNDF: UNDF-2026-000000469
# UNDF: (pending)
# quarkus-0003: BeanDeployment.recursiveBuild — transitive binding traversal no visited set O(2^D)
## CWE-407 — Algorithmic Complexity: Exponential Recursion on Diamond Annotation Hierarchy
| Field | Value |
|--------------|-------|
| ID | quarkus-0003 |
| Severity | MEDIUM |
| Ecosystem | quarkus |
| Package | arc/processor |
| File | `independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/BeanDeployment.java` |
| Lines | 955964 |
| Complexity | O(2^D) where D = diamond depth in annotation hierarchy |
| Hot path | Called at application startup for every binding annotation |
## Description
`BeanDeployment.recursiveBuild(DotName name, Map<DotName, Set<AnnotationInstance>> transitiveBindingsMap)`
traverses the transitive annotation binding graph recursively with no visited-set guard.
On a diamond-shaped annotation hierarchy (annotation A meta-annotated with @B and @C;
both @B and @C meta-annotated with @D), the call tree is:
```
recursiveBuild(A) → recursiveBuild(B) → recursiveBuild(D)
→ recursiveBuild(C) → recursiveBuild(D) ← D visited twice
```
At depth D, @D is visited 2^D times. At D=24: 16,777,216 redundant visits.
```java
// BEFORE — O(2^D): no visited set — exponential on diamond annotation hierarchies
private static Set<AnnotationInstance> recursiveBuild(DotName name,
Map<DotName, Set<AnnotationInstance>> transitiveBindingsMap) {
Set<AnnotationInstance> result = transitiveBindingsMap.get(name);
for (AnnotationInstance instance : transitiveBindingsMap.get(name)) {
if (transitiveBindingsMap.containsKey(instance.name())) {
result.addAll(recursiveBuild(instance.name(), transitiveBindingsMap));
}
}
return result;
}
```
Note: `result` is the same reference as `transitiveBindingsMap.get(name)` — the function
modifies the map's values in-place during iteration, which also carries a
`ConcurrentModificationException` risk if the Set implementation doesn't tolerate modification.
## Fix
Add a `Set<DotName> visited` parameter to guard against revisiting the same annotation node.
```java
// AFTER — O(N+E): visited set prevents exponential re-traversal
for (DotName name : result.keySet()) {
result.put(name, recursiveBuild(name, result, new HashSet<>()));
}
private static Set<AnnotationInstance> recursiveBuild(DotName name,
Map<DotName, Set<AnnotationInstance>> transitiveBindingsMap,
Set<DotName> visited) {
if (!visited.add(name)) {
return Collections.emptySet();
}
Set<AnnotationInstance> result = new HashSet<>(transitiveBindingsMap.get(name));
for (AnnotationInstance instance : transitiveBindingsMap.get(name)) {
if (transitiveBindingsMap.containsKey(instance.name())) {
result.addAll(recursiveBuild(instance.name(), transitiveBindingsMap, visited));
}
}
return result;
}
```
## Speedup
| Annotation depth (D) | Before (calls) | After (calls) | Speedup |
|---------------------|---------------|---------------|---------|
| 10 | 1,023 | 10 | 102× |
| 15 | 32,767 | 15 | 2,184× |
| 20 | 1,048,575 | 20 | 52,428× |
Growth before: O(2^D). Growth after: O(D).

View file

@ -1,3 +1,4 @@
# UNDF: UNDF-2026-000000341
## Classification
| Field | Value |