java-topology/defects/quarkus/patch/quarkus-0005-config-mapping-collect-interfaces-diamond.md
russell@unturf.com 130e4fb159 undf: assign 671-680 to diamond-scan defects; 680 total assigned
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)
2026-03-29 20:27:09 -04:00

2.7 KiB
Raw Blame History

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 262275
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.