hazelcast-0002/0003: ClassLoaderUtil + ProbeUtils diamond O(2^D); count 664→666
This commit is contained in:
parent
db463c1b4e
commit
1b0b3223b3
2 changed files with 147 additions and 0 deletions
|
|
@ -0,0 +1,70 @@
|
|||
# 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).
|
||||
|
|
@ -0,0 +1,77 @@
|
|||
# UNDF: (pending)
|
||||
# hazelcast-0003: ProbeUtils.flatten — O(2^D) diamond re-traversal; result.add() return value ignored before recursion
|
||||
|
||||
## CWE-407 — Algorithmic Complexity: O(2^D) recursive diamond re-traversal in metrics type flattening
|
||||
|
||||
| Field | Value |
|
||||
|--------------|-------|
|
||||
| ID | hazelcast-0003 |
|
||||
| Severity | MEDIUM |
|
||||
| Ecosystem | hazelcast |
|
||||
| Package | hazelcast |
|
||||
| File | `hazelcast/src/main/java/com/hazelcast/internal/metrics/impl/ProbeUtils.java` |
|
||||
| Lines | 28–39 |
|
||||
| Complexity | O(2^D) on diamond interface hierarchies |
|
||||
| Hot path | Called during metrics probe registration (`SourceMetadata`, `ProbeType`) — initialization/startup path |
|
||||
|
||||
## Defect
|
||||
|
||||
`ProbeUtils.flatten` recursively collects all classes and interfaces in a type's hierarchy into
|
||||
a `Collection<Class<?>> result` (backed by `LinkedHashSet` at both call sites). Although a Set
|
||||
is used, the recursion guard is MISSING — `result.add(clazz)` return value at the top of the
|
||||
method is IGNORED, so the method recurses into superclass AND all interfaces unconditionally even
|
||||
when `clazz` was already fully traversed:
|
||||
|
||||
```java
|
||||
// ProbeUtils.java:28-39 (DEFECT)
|
||||
static void flatten(final Class<?> clazz, final Collection<Class<?>> result) {
|
||||
result.add(clazz); // return value IGNORED
|
||||
|
||||
if (clazz.getSuperclass() != null) {
|
||||
flatten(clazz.getSuperclass(), result); // recurses unconditionally
|
||||
}
|
||||
|
||||
for (final Class<?> interfaze : clazz.getInterfaces()) {
|
||||
result.add(interfaze); // return value IGNORED
|
||||
flatten(interfaze, result); // DEFECT: recurses unconditionally
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
On a diamond (I1 and I2 both extend Base; clazz implements I1 and I2):
|
||||
- `flatten(I1)` adds Base to result; recurses into Base (traverses Base's hierarchy)
|
||||
- `flatten(I2)` attempts to add Base: `result.add(Base)` returns false (already present) — IGNORED;
|
||||
then `flatten(Base, result)` is called AGAIN → re-traverses Base's full hierarchy
|
||||
|
||||
At diamond depth D, Base is visited 2^D times.
|
||||
|
||||
Callers: `SourceMetadata` (line 50) and `ProbeType` (line 92) both use `LinkedHashSet`.
|
||||
|
||||
## Fix
|
||||
|
||||
Check `result.add()` return value at the top of the method to short-circuit already-visited nodes:
|
||||
|
||||
```java
|
||||
// AFTER — O(N+E) where N=types, E=hierarchy edges
|
||||
static void flatten(final Class<?> clazz, final Collection<Class<?>> result) {
|
||||
if (!result.add(clazz)) { return; } // already visited — skip entire subtree
|
||||
|
||||
if (clazz.getSuperclass() != null) {
|
||||
flatten(clazz.getSuperclass(), result);
|
||||
}
|
||||
|
||||
for (final Class<?> interfaze : clazz.getInterfaces()) {
|
||||
flatten(interfaze, result); // result.add happens at top of recursion
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Speedup
|
||||
|
||||
| Diamond depth (D) | Before (visits) | After (visits) | Speedup |
|
||||
|------------------|----------------|----------------|---------|
|
||||
| 5 | 31 | 5 | 6× |
|
||||
| 10 | 1,023 | 10 | 102× |
|
||||
| 15 | 32,767 | 15 | 2,184× |
|
||||
|
||||
Growth before: O(2^D). Growth after: O(D).
|
||||
Loading…
Add table
Add a link
Reference in a new issue