java-topology/defects/hazelcast/patch/hazelcast-0002-classloaderutil-addowninterfaces-diamond.md

3.1 KiB
Raw Blame History

UNDF: UNDF-2026-000000416

UNDF: (pending)

hazelcast-0002: ClassLoaderUtil.addOwnInterfaces — O(2^D) diamond re-traversal; Collections.addAll() return value ignored

CWE-407 — Algorithmic Complexity: O(2^D) recursive diamond re-traversal in class interface hierarchy collection

Field Value
ID hazelcast-0002
Severity HIGH
Ecosystem hazelcast
Package hazelcast
File hazelcast/src/main/java/com/hazelcast/internal/nio/ClassLoaderUtil.java
Lines 387393
Complexity O(2^D) on diamond interface hierarchies
Hot path Called from getAllInterfaces()implementsInterfaceWithSameName() during class loading

Defect

ClassLoaderUtil.addOwnInterfaces collects interfaces into a Collection<Class<?>> allInterfaces (backed by a HashSet at the call site in getAllInterfaces). Although items are added to a Set, the recursion guard is MISSING — Collections.addAll fills the set but the for loop recurses unconditionally, ignoring whether each interface was already present:

// ClassLoaderUtil.java:387-393 (DEFECT)
private static void addOwnInterfaces(Class<?> clazz, Collection<Class<?>> allInterfaces) {
    Class<?>[] interfaces = clazz.getInterfaces();
    Collections.addAll(allInterfaces, interfaces);   // adds items — return values IGNORED
    for (Class cl : interfaces) {
        addOwnInterfaces(cl, allInterfaces);         // DEFECT: recurses unconditionally
    }
}

On a diamond (I1 and I2 both extend Base; clazz implements I1 and I2):

  1. Collections.addAll(allInterfaces, [I1, I2]) → both added
  2. Loop: addOwnInterfaces(I1) → adds Base; recurses into Base
  3. Loop: addOwnInterfaces(I2)Collections.addAll(allInterfaces, [Base]) — add returns false (Base already present) — IGNORED; loop still recurses into Base again

At diamond depth D, Base is visited 2^D times.

This is a second distinct occurrence of the same pattern found in SerializationUtil.getInterfaces (hazelcast-0001) — both share the Collections.addAll + unconditional loop anti-pattern.

Fix

Replace Collections.addAll + unconditional loop with a per-element allInterfaces.add() guard:

// AFTER — O(N+E) where N=interfaces, E=inheritance edges
private static void addOwnInterfaces(Class<?> clazz, Collection<Class<?>> allInterfaces) {
    for (Class<?> cl : clazz.getInterfaces()) {
        if (allInterfaces.add(cl)) {          // add returns false if already present → skip
            addOwnInterfaces(cl, allInterfaces); // only recurse if newly added
        }
    }
}

Speedup

Diamond depth (D) Before (visits) After (visits) Speedup
5 31 5 6×
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).