71 lines
3.1 KiB
Markdown
71 lines
3.1 KiB
Markdown
# 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:
|
||
|
||
```java
|
||
// 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:
|
||
|
||
```java
|
||
// 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).
|