dagger-0001 + jackson-0001/0002: Deque.contains O(N×D) + two O(N²) type-hierarchy scans; count 656→659
This commit is contained in:
parent
7cb8abc236
commit
e610b42a21
4 changed files with 314 additions and 0 deletions
|
|
@ -0,0 +1,110 @@
|
|||
# UNDF: (pending)
|
||||
# jackson-0001: AnnotatedClassResolver._addSuperTypes/Interfaces — O(N²) custom _contains() list scan in type hierarchy collection
|
||||
|
||||
## CWE-407 — Algorithmic Complexity: O(N) custom list scan per type in recursive supertype/interface collection
|
||||
|
||||
| Field | Value |
|
||||
|--------------|-------|
|
||||
| ID | jackson-0001 |
|
||||
| Severity | MEDIUM |
|
||||
| Ecosystem | jackson-databind |
|
||||
| Package | com.fasterxml.jackson.databind |
|
||||
| File | `src/main/java/tools/jackson/databind/introspect/AnnotatedClassResolver.java` |
|
||||
| Lines | 164–216 |
|
||||
| Complexity | O(N) per type; O(N²) to collect N-type hierarchy |
|
||||
| Hot path | Called during Jackson class binding — once per deserialized/serialized class |
|
||||
|
||||
## Defect
|
||||
|
||||
`AnnotatedClassResolver._addSuperTypes` and `_addSuperInterfaces` collect the full type hierarchy into a
|
||||
`List<JavaType> result`. To avoid duplicates they call a custom `_contains(result, cls)` that performs an
|
||||
O(N) linear scan using reference equality on each element's raw class:
|
||||
|
||||
```java
|
||||
// AnnotatedClassResolver.java:164-216 (DEFECT)
|
||||
private static void _addSuperTypes(JavaType type, List<JavaType> result, boolean addClassItself) {
|
||||
// ...
|
||||
if (addClassItself) {
|
||||
if (_contains(result, cls)) { // O(N) linear scan
|
||||
return;
|
||||
}
|
||||
result.add(type);
|
||||
}
|
||||
for (JavaType intCls : type.getInterfaces()) {
|
||||
_addSuperInterfaces(intCls, result, true); // recurses
|
||||
}
|
||||
_addSuperTypes(type.getSuperClass(), result, true); // recurses
|
||||
}
|
||||
|
||||
private static void _addSuperInterfaces(JavaType type, List<JavaType> result, boolean addClassItself) {
|
||||
// ...
|
||||
if (addClassItself) {
|
||||
if (_contains(result, cls)) { // O(N) linear scan
|
||||
return;
|
||||
}
|
||||
result.add(type);
|
||||
// ...
|
||||
}
|
||||
for (JavaType intCls : type.getInterfaces()) {
|
||||
_addSuperInterfaces(intCls, result, true); // recurses
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean _contains(List<JavaType> found, Class<?> raw) {
|
||||
for (int i = 0, end = found.size(); i < end; ++i) {
|
||||
if (found.get(i).getRawClass() == raw) { // O(N) scan
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
```
|
||||
|
||||
For a type hierarchy with N supertypes/interfaces, `_contains` is called N times against a growing list,
|
||||
giving O(N²) total scan operations. Diamond hierarchies (interface A extends B and C; both extend D)
|
||||
are deduped correctly, but the dedup cost is O(N) per check.
|
||||
|
||||
## Fix
|
||||
|
||||
Add a parallel `Set<Class<?>>` for O(1) membership testing alongside the result list (which must be
|
||||
kept for ordered output):
|
||||
|
||||
```java
|
||||
// AFTER — O(N) total for N-type hierarchy
|
||||
// Entry point: allocate seen set
|
||||
private static List<JavaType> collectSuperTypes(JavaType type) {
|
||||
List<JavaType> result = new ArrayList<>();
|
||||
Set<Class<?>> seen = new HashSet<>(); // ADD: O(1) membership guard
|
||||
_addSuperTypes(type, result, seen, false);
|
||||
return result;
|
||||
}
|
||||
|
||||
private static void _addSuperTypes(JavaType type, List<JavaType> result, Set<Class<?>> seen,
|
||||
boolean addClassItself)
|
||||
{
|
||||
if (type == null) { return; }
|
||||
final Class<?> cls = type.getRawClass();
|
||||
if ((cls == CLS_OBJECT) || (cls == CLS_ENUM)) { return; }
|
||||
if (addClassItself) {
|
||||
if (!seen.add(cls)) { // O(1): add returns false if already present
|
||||
return;
|
||||
}
|
||||
result.add(type);
|
||||
}
|
||||
for (JavaType intCls : type.getInterfaces()) {
|
||||
_addSuperInterfaces(intCls, result, seen, true);
|
||||
}
|
||||
_addSuperTypes(type.getSuperClass(), result, seen, true);
|
||||
}
|
||||
// _contains() helper no longer needed
|
||||
```
|
||||
|
||||
## Speedup
|
||||
|
||||
| Types in hierarchy (N) | Before (comparisons) | After (comparisons) | Speedup |
|
||||
|------------------------|----------------------|---------------------|---------|
|
||||
| 10 | 55 | 10 | 5.5× |
|
||||
| 30 | 465 | 30 | 15.5× |
|
||||
| 50 | 1,275 | 50 | 25.5× |
|
||||
|
||||
Growth before: O(N²). Growth after: O(N).
|
||||
|
|
@ -0,0 +1,91 @@
|
|||
# UNDF: (pending)
|
||||
# jackson-0002: ClassUtil._addRawSuperTypes — ArrayList.contains() O(N²) in recursive supertype collection
|
||||
|
||||
## CWE-407 — Algorithmic Complexity: O(N) ArrayList.contains() per type in recursive class hierarchy traversal
|
||||
|
||||
| Field | Value |
|
||||
|--------------|-------|
|
||||
| ID | jackson-0002 |
|
||||
| Severity | MEDIUM |
|
||||
| Ecosystem | jackson-databind |
|
||||
| Package | com.fasterxml.jackson.databind |
|
||||
| File | `src/main/java/tools/jackson/databind/util/ClassUtil.java` |
|
||||
| Lines | 61–107 |
|
||||
| Complexity | O(N) per type; O(N²) to collect N-type hierarchy |
|
||||
| Hot path | Called from `findRawSuperTypes()` during mixin class resolution |
|
||||
|
||||
## Defect
|
||||
|
||||
`ClassUtil._addRawSuperTypes` recursively collects the full class/interface hierarchy. To deduplicate it
|
||||
calls `result.contains(cls)` where `result` is an `ArrayList<Class<?>>` — an O(N) linear scan:
|
||||
|
||||
```java
|
||||
// ClassUtil.java:61-107 (DEFECT)
|
||||
public static List<Class<?>> findRawSuperTypes(Class<?> cls, Class<?> endBefore, boolean addClassItself) {
|
||||
// ...
|
||||
List<Class<?>> result = new ArrayList<Class<?>>(8); // ArrayList → contains() is O(N)
|
||||
_addRawSuperTypes(cls, endBefore, result, addClassItself);
|
||||
return result;
|
||||
}
|
||||
|
||||
private static void _addRawSuperTypes(Class<?> cls, Class<?> endBefore,
|
||||
Collection<Class<?>> result, boolean addClassItself) {
|
||||
if (cls == endBefore || cls == null || cls == Object.class) { return; }
|
||||
if (addClassItself) {
|
||||
if (result.contains(cls)) { // O(N) linear scan
|
||||
return;
|
||||
}
|
||||
result.add(cls);
|
||||
}
|
||||
for (Class<?> intCls : cls.getInterfaces()) {
|
||||
_addRawSuperTypes(intCls, endBefore, result, true); // recurses into each interface
|
||||
}
|
||||
_addRawSuperTypes(cls.getSuperclass(), endBefore, result, true); // recurses up superclass chain
|
||||
}
|
||||
```
|
||||
|
||||
For a class hierarchy with N types (classes + interfaces), `result.contains()` is called N times against
|
||||
a growing ArrayList, giving O(N²) total comparisons. Diamond hierarchies (A implements B and C, both
|
||||
extend D) are correctly deduplicated, but the dedup cost is O(N) per call.
|
||||
|
||||
## Fix
|
||||
|
||||
Change `findRawSuperTypes` to use a `LinkedHashSet` internally for O(1) membership and preserved
|
||||
insertion order, then convert to List before returning:
|
||||
|
||||
```java
|
||||
// AFTER — O(N) total for N-type hierarchy
|
||||
public static List<Class<?>> findRawSuperTypes(Class<?> cls, Class<?> endBefore, boolean addClassItself) {
|
||||
if ((cls == null) || (cls == endBefore) || (cls == Object.class)) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
LinkedHashSet<Class<?>> result = new LinkedHashSet<>(8); // O(1) contains, preserves order
|
||||
_addRawSuperTypes(cls, endBefore, result, addClassItself);
|
||||
return new ArrayList<>(result);
|
||||
}
|
||||
|
||||
private static void _addRawSuperTypes(Class<?> cls, Class<?> endBefore,
|
||||
Collection<Class<?>> result, boolean addClassItself) {
|
||||
if (cls == endBefore || cls == null || cls == Object.class) { return; }
|
||||
if (addClassItself) {
|
||||
if (!result.add(cls)) { // O(1): LinkedHashSet.add() returns false if already present
|
||||
return;
|
||||
}
|
||||
// No separate result.add(cls) needed — add() already inserted it
|
||||
}
|
||||
for (Class<?> intCls : cls.getInterfaces()) {
|
||||
_addRawSuperTypes(intCls, endBefore, result, true);
|
||||
}
|
||||
_addRawSuperTypes(cls.getSuperclass(), endBefore, result, true);
|
||||
}
|
||||
```
|
||||
|
||||
## Speedup
|
||||
|
||||
| Types in hierarchy (N) | Before (comparisons) | After (comparisons) | Speedup |
|
||||
|------------------------|----------------------|---------------------|---------|
|
||||
| 10 | 55 | 10 | 5.5× |
|
||||
| 30 | 465 | 30 | 15.5× |
|
||||
| 50 | 1,275 | 50 | 25.5× |
|
||||
|
||||
Growth before: O(N²). Growth after: O(N).
|
||||
Loading…
Add table
Add a link
Reference in a new issue