# UNDF: UNDF-2026-000000415
# UNDF: (pending)
# hazelcast-0001: SerializationUtil.getInterfaces — O(2^D) diamond re-traversal; Collections.addAll() return value ignored

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

| Field        | Value |
|--------------|-------|
| ID           | hazelcast-0001 |
| Severity     | HIGH |
| Ecosystem    | hazelcast |
| Package      | hazelcast |
| File         | `hazelcast/src/main/java/com/hazelcast/internal/serialization/impl/SerializationUtil.java` |
| Lines        | 150–158 |
| Complexity   | O(2^D) on diamond interface hierarchies |
| Hot path     | Called per serialization of a type not yet seen by the serialization service (first-time serializer lookup) |

## Defect

`SerializationUtil.getInterfaces` recursively collects all interfaces of a class into a `Set<Class<?>>`.
Although it uses a Set, the recursion guard is MISSING — `Collections.addAll(interfaces, classes)` fills
the set but the `for` loop then recurses into every interface unconditionally, regardless of whether
each was already present in the set:

```java
// SerializationUtil.java:150-158 (DEFECT)
static void getInterfaces(Class<?> clazz, Set<Class<?>> interfaces) {
    final var classes = clazz.getInterfaces();
    if (classes.length > 0) {
        Collections.addAll(interfaces, classes);    // adds to set (ignored return vals)
        for (Class<?> cl : classes) {
            getInterfaces(cl, interfaces);           // DEFECT: recurses unconditionally
        }                                            // ignores whether cl was already present
    }
}
```

On a diamond hierarchy (interface I1 extends Base; interface I2 extends Base; class C implements I1, I2):
- `getInterfaces(C)`:
  - `Collections.addAll(interfaces, [I1, I2])` → both added
  - recurse into I1: `getInterfaces(I1)`:
    - adds Base to set, recurses into Base
  - recurse into I2: `getInterfaces(I2)`:
    - `Collections.addAll(interfaces, [Base])` → Base already present, add() returns false (IGNORED)
    - STILL recurses into Base → unnecessary re-traversal

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

Called from `AbstractSerializationService.lookupCustomSerializer()` (line 663, and again once per
superclass in the superclass chain at line 669), which is invoked on every first serialization
of a previously-unseen class type.

## Fix

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

```java
// AFTER — O(N+E) where N=interfaces, E=inheritance edges
static void getInterfaces(Class<?> clazz, Set<Class<?>> interfaces) {
    for (Class<?> cl : clazz.getInterfaces()) {
        if (interfaces.add(cl)) {           // add returns false if already present → skip
            getInterfaces(cl, interfaces);  // 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).
