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)
2.7 KiB
2.7 KiB
UNDF: UNDF-2026-000000527
UNDF: (pending)
quarkus-0005: ConfigMappingUtils.collectInterfacesRec — O(2^D) diamond interface re-traversal
CWE-407 — Algorithmic Complexity: O(2^D) recursive diamond re-traversal in config interface collection
| Field | Value |
|---|---|
| ID | quarkus-0005 |
| Severity | HIGH |
| Ecosystem | quarkus |
| Package | io.quarkus.deployment.configuration |
| File | core/deployment/src/main/java/io/quarkus/deployment/configuration/ConfigMappingUtils.java |
| Lines | 262–275 |
| Complexity | O(2^D) on diamond interface hierarchies |
| Hot path | Quarkus build-time config mapping discovery (startup) |
Defect
private static void collectInterfacesRec(ClassInfo current, IndexView index, Set<DotName> result) {
List<DotName> interfaces = current.interfaceNames();
if (interfaces.isEmpty()) {
return;
}
for (DotName iface : interfaces) {
ClassInfo classByName = index.getClassByName(iface);
if (classByName == null) {
continue; // just ignore this type
}
result.add(iface); // return value ignored
collectInterfacesRec(classByName, index, result); // unconditional recursion
}
}
result.add(iface) returns false when iface is already in the set (diamond topology), but
the return value is discarded. collectInterfacesRec recurses unconditionally into classByName
regardless. In a depth-D diamond hierarchy (C implements I1 and I2, both extend I3) the
shared ancestor I3 is traversed 2^(D-1) times.
Fix
Guard the recursion on the return value of Set.add:
private static void collectInterfacesRec(ClassInfo current, IndexView index, Set<DotName> result) {
List<DotName> interfaces = current.interfaceNames();
if (interfaces.isEmpty()) {
return;
}
for (DotName iface : interfaces) {
ClassInfo classByName = index.getClassByName(iface);
if (classByName == null) {
continue; // just ignore this type
}
if (result.add(iface)) { // guard: skip if already visited
collectInterfacesRec(classByName, index, result);
}
}
}
Speedup
| D | Nodes visited (before) | Nodes visited (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 runs during Quarkus build-time deployment processing of @ConfigMapping interfaces.
Config interfaces with shared parent interfaces (common in layered config hierarchies) trigger
the diamond case. At D=10 the difference is 1000× — critical for build performance and memory.