tomcat-0001/0002: BeanSupportFull/Standalone populateFromInterfaces diamond O(2^D); count 666→668
This commit is contained in:
parent
8b8f7ee951
commit
cedcc9d5ee
3 changed files with 192 additions and 0 deletions
12
defects/activemq/patch/CLEAN.md
Normal file
12
defects/activemq/patch/CLEAN.md
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
# CLEAN — Apache ActiveMQ Classic
|
||||
|
||||
Scanned 2026-03-29 for CWE-407 (algorithmic complexity).
|
||||
|
||||
## Findings
|
||||
|
||||
- `IntrospectionSupport.java` — CLEAN: reflection on methods only, no recursive class traversal
|
||||
- `AnnotatedMBean.java` — CLEAN: simple non-recursive `getInterfaces()` iteration
|
||||
- `AsyncAnnotatedMBean.java` — CLEAN: same pattern, non-recursive
|
||||
- OpenWire generator files — CLEAN: code generation context, no runtime recursion
|
||||
|
||||
**Result: No actionable CWE-407 defects.**
|
||||
|
|
@ -0,0 +1,94 @@
|
|||
# UNDF: (pending)
|
||||
# tomcat-0001: BeanSupportFull.populateFromInterfaces — O(2^D) diamond re-traversal without visited set
|
||||
|
||||
## CWE-407 — Algorithmic Complexity: O(2^D) recursive diamond re-traversal in Jakarta EL bean property collection
|
||||
|
||||
| Field | Value |
|
||||
|--------------|-------|
|
||||
| ID | tomcat-0001 |
|
||||
| Severity | MEDIUM |
|
||||
| Ecosystem | tomcat |
|
||||
| Package | jakarta.el |
|
||||
| File | `java/jakarta/el/BeanSupportFull.java` |
|
||||
| Lines | 57–73 |
|
||||
| Complexity | O(2^D) on diamond interface hierarchies |
|
||||
| Hot path | Called during EL expression evaluation for first access to a bean type (cached after first use) |
|
||||
|
||||
## Defect
|
||||
|
||||
`BeanSupportFull.populateFromInterfaces` recursively collects bean property descriptors from a class's
|
||||
full interface hierarchy. It has NO visited set — it recurses unconditionally into every interface and
|
||||
every superclass, causing diamond hierarchies to be traversed exponentially:
|
||||
|
||||
```java
|
||||
// BeanSupportFull.java:57-73 (DEFECT)
|
||||
private void populateFromInterfaces(Class<?> aClass) throws IntrospectionException {
|
||||
Class<?>[] interfaces = aClass.getInterfaces();
|
||||
for (Class<?> ifs : interfaces) {
|
||||
BeanInfo info = Introspector.getBeanInfo(ifs);
|
||||
PropertyDescriptor[] pds = info.getPropertyDescriptors();
|
||||
for (PropertyDescriptor pd : pds) {
|
||||
if (!this.properties.containsKey(pd.getName())) {
|
||||
this.properties.put(pd.getName(), new BeanPropertyFull(this.type, pd));
|
||||
}
|
||||
}
|
||||
populateFromInterfaces(ifs); // DEFECT: unconditional recursion — no visited guard
|
||||
}
|
||||
Class<?> superclass = aClass.getSuperclass();
|
||||
if (superclass != null) {
|
||||
populateFromInterfaces(superclass); // DEFECT: unconditional recursion — no visited guard
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
On a diamond (I1 and I2 both extend Base; class C implements I1 and I2):
|
||||
- `populateFromInterfaces(C)`:
|
||||
- Recurse into I1: `populateFromInterfaces(I1)` → processes Base, recurses into Base's interfaces
|
||||
- Recurse into I2: `populateFromInterfaces(I2)` → processes Base AGAIN, recurses into Base's interfaces AGAIN
|
||||
|
||||
The property dedup at line 63 (`properties.containsKey`) prevents duplicate property additions but does
|
||||
NOT prevent re-traversal — the recursive calls at lines 67 and 71 still happen. Every `Introspector.getBeanInfo(ifs)` and subsequent loop re-executes for already-visited nodes.
|
||||
|
||||
At diamond depth D, Base is traversed 2^D times.
|
||||
|
||||
## Fix
|
||||
|
||||
Add a `Set<Class<?>> visited` parameter with a public wrapper:
|
||||
|
||||
```java
|
||||
// AFTER — O(N+E) where N=interfaces, E=hierarchy edges
|
||||
private void populateFromInterfaces(Class<?> aClass) throws IntrospectionException {
|
||||
populateFromInterfaces(aClass, new HashSet<>());
|
||||
}
|
||||
|
||||
private void populateFromInterfaces(Class<?> aClass, Set<Class<?>> visited)
|
||||
throws IntrospectionException {
|
||||
if (!visited.add(aClass)) { return; } // skip already-traversed types
|
||||
|
||||
Class<?>[] interfaces = aClass.getInterfaces();
|
||||
for (Class<?> ifs : interfaces) {
|
||||
BeanInfo info = Introspector.getBeanInfo(ifs);
|
||||
PropertyDescriptor[] pds = info.getPropertyDescriptors();
|
||||
for (PropertyDescriptor pd : pds) {
|
||||
if (!this.properties.containsKey(pd.getName())) {
|
||||
this.properties.put(pd.getName(), new BeanPropertyFull(this.type, pd));
|
||||
}
|
||||
}
|
||||
populateFromInterfaces(ifs, visited);
|
||||
}
|
||||
Class<?> superclass = aClass.getSuperclass();
|
||||
if (superclass != null) {
|
||||
populateFromInterfaces(superclass, visited);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Speedup
|
||||
|
||||
| Diamond depth (D) | Before (traversals) | After (traversals) | 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).
|
||||
|
|
@ -0,0 +1,86 @@
|
|||
# UNDF: (pending)
|
||||
# tomcat-0002: BeanSupportStandalone.populateFromInterfaces — O(2^D) diamond re-traversal without visited set
|
||||
|
||||
## CWE-407 — Algorithmic Complexity: O(2^D) recursive diamond re-traversal in standalone Jakarta EL bean property collection
|
||||
|
||||
| Field | Value |
|
||||
|--------------|-------|
|
||||
| ID | tomcat-0002 |
|
||||
| Severity | MEDIUM |
|
||||
| Ecosystem | tomcat |
|
||||
| Package | jakarta.el |
|
||||
| File | `java/jakarta/el/BeanSupportStandalone.java` |
|
||||
| Lines | 181–196 |
|
||||
| Complexity | O(2^D) on diamond interface hierarchies |
|
||||
| Hot path | Called during EL bean property discovery when not using full Introspector (standalone/embedded mode) |
|
||||
|
||||
## Defect
|
||||
|
||||
`BeanSupportStandalone.populateFromInterfaces` has the same diamond re-traversal defect as
|
||||
`BeanSupportFull.populateFromInterfaces` (tomcat-0001). No visited set; recursion is unconditional:
|
||||
|
||||
```java
|
||||
// BeanSupportStandalone.java:181-196 (DEFECT)
|
||||
private void populateFromInterfaces(Class<?> aClass) {
|
||||
Class<?>[] interfaces = aClass.getInterfaces();
|
||||
for (Class<?> ifs : interfaces) {
|
||||
PropertyDescriptor[] pds = getPropertyDescriptors(type); // note: uses 'type', not 'ifs'
|
||||
for (PropertyDescriptor pd : pds) {
|
||||
if (!this.properties.containsKey(pd.getName())) {
|
||||
this.properties.put(pd.getName(), new BeanPropertyStandalone(this.type, pd));
|
||||
}
|
||||
}
|
||||
populateFromInterfaces(ifs); // DEFECT: unconditional recursion — no visited guard
|
||||
}
|
||||
Class<?> superclass = aClass.getSuperclass();
|
||||
if (superclass != null) {
|
||||
populateFromInterfaces(superclass); // DEFECT: unconditional recursion — no visited guard
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
On a diamond hierarchy, the `populateFromInterfaces` recursion visits shared ancestor nodes 2^D times.
|
||||
The property dedup at line 186 (`properties.containsKey`) prevents duplicate properties but does NOT
|
||||
prevent the re-traversal cost — all recursive calls happen regardless.
|
||||
|
||||
Same root cause as tomcat-0001; independent occurrence in the standalone implementation.
|
||||
|
||||
## Fix
|
||||
|
||||
Add a `Set<Class<?>> visited` parameter with a public wrapper:
|
||||
|
||||
```java
|
||||
// AFTER — O(N+E) where N=interfaces, E=hierarchy edges
|
||||
private void populateFromInterfaces(Class<?> aClass) {
|
||||
populateFromInterfaces(aClass, new HashSet<>());
|
||||
}
|
||||
|
||||
private void populateFromInterfaces(Class<?> aClass, Set<Class<?>> visited) {
|
||||
if (!visited.add(aClass)) { return; } // skip already-traversed types
|
||||
|
||||
Class<?>[] interfaces = aClass.getInterfaces();
|
||||
for (Class<?> ifs : interfaces) {
|
||||
PropertyDescriptor[] pds = getPropertyDescriptors(ifs); // also fix: use ifs, not type
|
||||
for (PropertyDescriptor pd : pds) {
|
||||
if (!this.properties.containsKey(pd.getName())) {
|
||||
this.properties.put(pd.getName(), new BeanPropertyStandalone(this.type, pd));
|
||||
}
|
||||
}
|
||||
populateFromInterfaces(ifs, visited);
|
||||
}
|
||||
Class<?> superclass = aClass.getSuperclass();
|
||||
if (superclass != null) {
|
||||
populateFromInterfaces(superclass, visited);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Speedup
|
||||
|
||||
| Diamond depth (D) | Before (traversals) | After (traversals) | 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