New defects from diamond O(2^D) sweep: - cpython-0002/0003/0004: pydoc.allmethods, turtle.__methodDict, idlelib.rpc._getmethods - micronaut-0005/0006/0007: populateTypeHierarchy, populateTypeArgumentsForInterfaces, SuperclassAwareTypeVisitor - quarkus-0004/0005: HierarchyDiscovery.discoverTypes, ConfigMappingUtils.collectInterfacesRec - weld-0004/0005: HierarchyDiscovery.discoverTypes, Services.identifyServiceInterfaces - rails-0019: Digestor#dependency_digest Array#include? O(N²) - spring-0007: AnnotationsScanner.processClassHierarchy O(2^D) - django-0007: migrations.state.flatten_bases O(2^D) - typescript-0005: hasBaseType O(2^D) - hibernate-validator-0003: ClassHierarchyHelper.getImplementedInterfaces O(2^D) - swift-0001: QualifiedLookupRequest::evaluate protocol superclass O(2^D)
4 KiB
UNDF: UNDF-2026-000000672
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
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:
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.