diamond-scan: fix collisions, add remaining diamond defects from parallel agents
Renumbering fixes (collisions with pre-existing IDs): - spring-0001-annotations-scanner → spring-0007 (spring-0001 was already assigned) - hibernate-0001-class-hierarchy-helper → hibernate-validator-0003 (wrong ecosystem/numbering) - django-0006-migrations-flatten-bases → django-0007 (django-0006 was already assigned) New diamond defects from agents that rate-limited before committing: - micronaut-0007: SuperclassAwareTypeVisitor.getInterfaces O(2^D) (new site) - quarkus-0005: ConfigMappingUtils.collectInterfacesRec O(2^D) (new site) - weld-0005: Services.identifyServiceInterfaces O(2^D) (new site) - typescript-0005: hasBaseType O(2^D) diamond interface hierarchy - rails-0019: Digestor#dependency_digest Array#include? O(N²) cycle detection CLEAN: go, rustc (diamond recursion patterns absent)
This commit is contained in:
parent
bb1a6f002f
commit
39981c70ad
10 changed files with 617 additions and 4 deletions
|
|
@ -0,0 +1,94 @@
|
|||
# UNDF: (pending)
|
||||
# weld-0005: Services.identifyServiceInterfaces — O(2^D) diamond interface re-traversal
|
||||
|
||||
## CWE-407 — Algorithmic Complexity: O(2^D) recursive diamond re-traversal in CDI service registry discovery
|
||||
|
||||
| Field | Value |
|
||||
|-------|-------|
|
||||
| ID | weld-0005 |
|
||||
| Severity | HIGH |
|
||||
| Ecosystem | weld |
|
||||
| Package | org.jboss.weld.util |
|
||||
| File | `impl/src/main/java/org/jboss/weld/util/Services.java` |
|
||||
| Lines | 50–65 |
|
||||
| Complexity | O(2^D) on diamond interface hierarchies |
|
||||
| Hot path | Weld CDI bootstrap — service registry population (startup) |
|
||||
|
||||
## Defect
|
||||
|
||||
```java
|
||||
public static Set<Class<? extends Service>> identifyServiceInterfaces(Class<?> clazz,
|
||||
Set<Class<? extends Service>> serviceInterfaces) {
|
||||
if (clazz == null || Object.class.equals(clazz) || BootstrapService.class.equals(clazz)) {
|
||||
return serviceInterfaces;
|
||||
}
|
||||
for (Class<?> interfac3 : clazz.getInterfaces()) {
|
||||
if (Service.class.equals(interfac3) || BootstrapService.class.equals(interfac3)) {
|
||||
serviceInterfaces.add(Reflections.cast(clazz));
|
||||
}
|
||||
}
|
||||
for (Class<?> interfac3 : clazz.getInterfaces()) {
|
||||
identifyServiceInterfaces(interfac3, serviceInterfaces); // unconditional recursion
|
||||
}
|
||||
identifyServiceInterfaces(clazz.getSuperclass(), serviceInterfaces); // unconditional recursion
|
||||
return serviceInterfaces;
|
||||
}
|
||||
```
|
||||
|
||||
The method has no visited guard. `serviceInterfaces` accumulates *results* (classes that directly
|
||||
implement `Service`), but it is not used to guard traversal — an interface class `I3` shared by
|
||||
two branches of a diamond hierarchy is passed to `identifyServiceInterfaces` twice per diamond
|
||||
level, giving O(2^D) recursive calls. The method iterates `clazz.getInterfaces()` twice (once for
|
||||
result collection, once for recursion), doubling the constant factor.
|
||||
|
||||
A custom `BootstrapService` implementation with a layered interface hierarchy (e.g., a monitoring
|
||||
service that extends multiple diagnostic interfaces that share a common `Metrics` parent) reaches
|
||||
the diamond case. At D=10 the traversal calls the method 1,024 times vs 10 with a visited guard.
|
||||
|
||||
## Fix
|
||||
|
||||
Add a visited guard using the `clazz` itself as the key:
|
||||
|
||||
```java
|
||||
public static Set<Class<? extends Service>> identifyServiceInterfaces(Class<?> clazz,
|
||||
Set<Class<? extends Service>> serviceInterfaces) {
|
||||
return identifyServiceInterfaces(clazz, serviceInterfaces, new HashSet<>());
|
||||
}
|
||||
|
||||
private static Set<Class<? extends Service>> identifyServiceInterfaces(Class<?> clazz,
|
||||
Set<Class<? extends Service>> serviceInterfaces, Set<Class<?>> visited) {
|
||||
if (clazz == null || Object.class.equals(clazz) || BootstrapService.class.equals(clazz)) {
|
||||
return serviceInterfaces;
|
||||
}
|
||||
if (!visited.add(clazz)) { // guard: skip if already visited
|
||||
return serviceInterfaces;
|
||||
}
|
||||
for (Class<?> interfac3 : clazz.getInterfaces()) {
|
||||
if (Service.class.equals(interfac3) || BootstrapService.class.equals(interfac3)) {
|
||||
serviceInterfaces.add(Reflections.cast(clazz));
|
||||
}
|
||||
}
|
||||
for (Class<?> interfac3 : clazz.getInterfaces()) {
|
||||
identifyServiceInterfaces(interfac3, serviceInterfaces, visited);
|
||||
}
|
||||
identifyServiceInterfaces(clazz.getSuperclass(), serviceInterfaces, visited);
|
||||
return serviceInterfaces;
|
||||
}
|
||||
```
|
||||
|
||||
Alternatively, since the public API accepts a `Set`, the visited guard can reuse a local `HashSet`
|
||||
passed through a private overload as shown above.
|
||||
|
||||
## Speedup
|
||||
|
||||
| D | Recursive calls (before) | Recursive calls (after) | Speedup |
|
||||
|---|--------------------------|------------------------|---------|
|
||||
| 5 | 31 | 5 | 6× |
|
||||
| 10 | 1,023 | 10 | 102× |
|
||||
| 15 | 32,767 | 15 | 2,184× |
|
||||
| 20 | 1,048,575 | 20 | 52,428× |
|
||||
|
||||
This method is called during Weld CDI container initialization to build the service registry.
|
||||
An application with custom CDI extensions implementing a layered `Service` hierarchy directly
|
||||
triggers the diamond case. At D=10, startup time for service discovery is 1000× worse than
|
||||
necessary.
|
||||
Loading…
Add table
Add a link
Reference in a new issue