3.1 KiB
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 | 387–393 |
| 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):
Collections.addAll(allInterfaces, [I1, I2])→ both added- Loop:
addOwnInterfaces(I1)→ adds Base; recurses into Base - 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).