java-topology/defects/spring/patch/spring-0001-beanfactoryutils-mergenames-list-contains.md
russell@unturf.com 068ebbd29f cpp-systems: tor CLEAN.md updated to note existing patches tor-0001/0002/0003
Scanned bitcoin/dragonfly/tor/transmission/nmap/ceph/allegro5 for additional
CWE-407 defects. All repos found CLEAN beyond previously recorded patches.
Updated tor/CLEAN.md to correctly reference existing tor-0001 through tor-0003.
2026-03-29 19:54:59 -04:00

102 lines
3.7 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# UNDF: UNDF-2026-000000295
# UNDF: (pending)
# spring-0001: BeanFactoryUtils.mergeNamesWithParent — O(P×R) ArrayList.contains inside loop
## CWE-407 — Algorithmic Complexity: Unnecessary Quadratic Complexity (List membership inside loop)
| Field | Value |
|-------|-------|
| ID | spring-0001 |
| Severity | MEDIUM |
| Ecosystem | spring-framework |
| Package | org.springframework.beans.factory |
| File | `spring-beans/src/main/java/org/springframework/beans/factory/BeanFactoryUtils.java` |
| Lines | 525532 |
| Complexity | O(P×R) |
| Hot path | bean type resolution with hierarchical ApplicationContext |
## Defect
`BeanFactoryUtils.mergeNamesWithParent()` is called by
`beanNamesForTypeIncludingAncestors()` and related methods to merge bean name
lists from parent and child application contexts. The `merged` variable is an
`ArrayList<String>`. For each of the P entries in `parentResult`, the code calls
`merged.contains(beanName)` — an O(R) linear scan where R is the number of
already-added names. Total cost O(P×R).
```java
// BeanFactoryUtils.java line 521-532
private static String[] mergeNamesWithParent(String[] result, String[] parentResult,
HierarchicalBeanFactory hbf) {
if (parentResult.length == 0) {
return result;
}
List<String> merged = new ArrayList<>(result.length + parentResult.length);
merged.addAll(Arrays.asList(result));
for (String beanName : parentResult) {
if (!merged.contains(beanName) && !hbf.containsLocalBean(beanName)) {
// ^^^^^^^^ O(R) per iteration → O(P×R) total
merged.add(beanName);
}
}
return StringUtils.toStringArray(merged);
}
```
This method is called from `getBeanNamesForType()`, `beanNamesForAnnotationIncludingAncestors()`,
and similar utility methods which can be called at runtime (e.g., during dependency injection,
AOP proxy creation, and Spring Boot auto-configuration) with deep ApplicationContext hierarchies.
## Fix
Use a `LinkedHashSet` to preserve insertion order while providing O(1) membership tests,
or build a `HashSet` for the dedup check:
```java
private static String[] mergeNamesWithParent(String[] result, String[] parentResult,
HierarchicalBeanFactory hbf) {
if (parentResult.length == 0) {
return result;
}
Set<String> seen = new HashSet<>(Arrays.asList(result));
List<String> merged = new ArrayList<>(result.length + parentResult.length);
merged.addAll(Arrays.asList(result));
for (String beanName : parentResult) {
if (!seen.contains(beanName) && !hbf.containsLocalBean(beanName)) {
seen.add(beanName);
merged.add(beanName);
}
}
return StringUtils.toStringArray(merged);
}
```
Alternatively, use `LinkedHashSet` directly:
```java
private static String[] mergeNamesWithParent(String[] result, String[] parentResult,
HierarchicalBeanFactory hbf) {
if (parentResult.length == 0) {
return result;
}
Set<String> merged = new LinkedHashSet<>(Arrays.asList(result));
for (String beanName : parentResult) {
if (!merged.contains(beanName) && !hbf.containsLocalBean(beanName)) {
merged.add(beanName);
}
}
return StringUtils.toStringArray(merged);
}
```
## Speedup
| R (result count) | P (parentResult count) | Before (ops) | After (ops) | Speedup |
|-----------------|------------------------|-------------|-------------|---------|
| 100 | 100 | 10,000 | 100 | 100× |
| 500 | 500 | 250,000 | 500 | 500× |
| 1,000 | 1,000 | 1,000,000 | 1,000 | 1,000× |
Applications with large numbers of beans and deep ApplicationContext hierarchies
(common in Spring Boot multi-module applications and OSGi container deployments)
are most affected.