openssl-0004 + uwsgi-0001: 2 new defects (500x/249x); all 10 missing whitepaper entries restored; count 590→592
This commit is contained in:
parent
2e4f7807d5
commit
34e8d9212f
18 changed files with 1366 additions and 4 deletions
26
defects/gunicorn/patch/gunicorn-CLEAN.md
Normal file
26
defects/gunicorn/patch/gunicorn-CLEAN.md
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
# gunicorn — CWE-407 Scan: CLEAN
|
||||
|
||||
**Date:** 2026-03-27
|
||||
**Scope:** Full Python codebase (`gunicorn/`)
|
||||
|
||||
## Findings
|
||||
|
||||
No CWE-407 defects confirmed.
|
||||
|
||||
### Patterns examined
|
||||
|
||||
| Location | Pattern | Verdict |
|
||||
|----------|---------|---------|
|
||||
| `gunicorn/util.py:hop_headers` | `header.lower().strip() in hop_headers` | CLEAN — `hop_headers` is a `frozenset` (O(1)) |
|
||||
| `gunicorn/http/message.py:parse_headers` | `name in forwarder_headers` inside header loop | BOUNDED — `forwarder_headers` is a list but default length is 2 ("SCRIPT_NAME,PATH_INFO"); not exploitable |
|
||||
| `gunicorn/http/message.py:secure_scheme_headers` | `name in secure_scheme_headers` inside header loop | CLEAN — `secure_scheme_headers` is a `dict` (O(1)) |
|
||||
| `gunicorn/arbiter.py:WORKERS` | worker management | CLEAN — `WORKERS` is a `dict` keyed by PID |
|
||||
| `gunicorn/http2/stream.py` | HTTP/2 stream state | CLEAN — simple state machine, no list-based membership |
|
||||
| `gunicorn/http2/connection.py` | `headers` list iteration | CLEAN — single-pass iteration, no inner scan |
|
||||
| `gunicorn/config.py` | config validation | CLEAN — all O(1) structures or bounded lists |
|
||||
|
||||
## Conclusion
|
||||
|
||||
Gunicorn uses appropriate data structures throughout hot paths. The
|
||||
`forwarder_headers` list is user-controlled and defaults to 2 elements,
|
||||
making any quadratic component negligible in practice.
|
||||
|
|
@ -0,0 +1,133 @@
|
|||
# UNDF: UNDF-2026-000000401
|
||||
# hibernate-0006: AbstractEntityPersister — O(T²) alias dedup in subclass property closure
|
||||
|
||||
## CWE-407 — Algorithmic Complexity: Linear Membership Test in Loop
|
||||
|
||||
| Field | Value |
|
||||
|--------------|-------|
|
||||
| ID | hibernate-0006 |
|
||||
| Severity | MEDIUM |
|
||||
| Ecosystem | hibernate-orm |
|
||||
| Package | hibernate-core |
|
||||
| File | `hibernate-core/src/main/java/org/hibernate/persister/entity/AbstractEntityPersister.java` |
|
||||
| Lines | 665–709 |
|
||||
| Complexity | O(T²) where T = total columns across subclass property closure |
|
||||
| Fix | Replace `ArrayList<String>` with `LinkedHashSet<String>` for alias dedup |
|
||||
|
||||
## Defective Code
|
||||
|
||||
```java
|
||||
// Lines 665-709 in AbstractEntityPersister constructor
|
||||
final ArrayList<String> aliases = new ArrayList<>(); // O(N) contains
|
||||
final ArrayList<String> formulaAliases = new ArrayList<>(); // O(N) contains
|
||||
|
||||
for (var prop : persistentClass.getSubclassPropertyClosure()) { // outer: O(P properties)
|
||||
...
|
||||
for (int i = 0; i < selectables.size(); i++) { // inner: O(C columns/prop)
|
||||
...
|
||||
final String columnAlias = selectable.getAlias(...);
|
||||
if (prop.isSelectable() && !aliases.contains(columnAlias)) { // O(T) — CWE-407
|
||||
aliases.add(columnAlias);
|
||||
}
|
||||
...
|
||||
final String formulaAlias = selectable.getAlias(dialect);
|
||||
if (prop.isSelectable() && !formulaAliases.contains(formulaAlias)) { // O(F) — CWE-407
|
||||
formulaAliases.add(formulaAlias);
|
||||
}
|
||||
}
|
||||
}
|
||||
subclassColumnAliasClosure = toStringArray(aliases);
|
||||
subclassFormulaAliasClosure = toStringArray(formulaAliases);
|
||||
```
|
||||
|
||||
**Pattern:** For each column selectable (total T across all subclass properties), calls
|
||||
`ArrayList.contains()` on a list that grows to size T. Total cost: **O(T²)**.
|
||||
|
||||
For table-per-hierarchy (TPH) inheritance with many subclasses (e.g., 50 subclasses × 20
|
||||
columns each = 1000 total selectables), this creates a 1,000,000 operation dedup phase
|
||||
during `SessionFactory` startup.
|
||||
|
||||
## Complexity Table
|
||||
|
||||
| T (total subclass columns) | Operations (before) | Operations (after) |
|
||||
|---------------------------|---------------------|-------------------|
|
||||
| 100 | ~10,000 | ~100 |
|
||||
| 500 | ~250,000 | ~500 |
|
||||
| 1,000 | ~1,000,000 | ~1,000 |
|
||||
|
||||
Speedup ratio: **~1000x** at T=1000.
|
||||
|
||||
## Fix
|
||||
|
||||
```java
|
||||
// hibernate-0006 fix: LinkedHashSet for O(1) contains() + insertion-order preservation.
|
||||
// Previously ArrayList<String>: each aliases.contains() is O(T) in a loop over T columns
|
||||
// → O(T²) total. LinkedHashSet.add() is O(1) amortized and handles dedup automatically.
|
||||
final LinkedHashSet<String> aliases = new LinkedHashSet<>();
|
||||
final LinkedHashSet<String> formulaAliases = new LinkedHashSet<>();
|
||||
|
||||
for (var prop : persistentClass.getSubclassPropertyClosure()) {
|
||||
...
|
||||
for (int i = 0; i < selectables.size(); i++) {
|
||||
...
|
||||
if (selectable instanceof Formula) {
|
||||
...
|
||||
if (prop.isSelectable()) {
|
||||
formulaAliases.add(formulaAlias); // O(1) — dedup handled by set
|
||||
}
|
||||
}
|
||||
else if (selectable instanceof Column column) {
|
||||
...
|
||||
if (prop.isSelectable()) {
|
||||
aliases.add(columnAlias); // O(1) — dedup handled by set
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
subclassColumnAliasClosure = toStringArray(new ArrayList<>(aliases));
|
||||
subclassFormulaAliasClosure = toStringArray(new ArrayList<>(formulaAliases));
|
||||
```
|
||||
|
||||
## Patch
|
||||
|
||||
```diff
|
||||
--- a/hibernate-core/src/main/java/org/hibernate/persister/entity/AbstractEntityPersister.java
|
||||
+++ b/hibernate-core/src/main/java/org/hibernate/persister/entity/AbstractEntityPersister.java
|
||||
@@ -xxx import section +++
|
||||
+import java.util.LinkedHashSet;
|
||||
|
||||
@@ -664,8 +664,11 @@ public abstract class AbstractEntityPersister
|
||||
// SUBCLASS PROPERTY CLOSURE
|
||||
- final ArrayList<String> aliases = new ArrayList<>();
|
||||
- final ArrayList<String> formulaAliases = new ArrayList<>();
|
||||
+ // hibernate-0006 fix: LinkedHashSet for O(1) dedup; was O(T²) with ArrayList.contains()
|
||||
+ // where T = total columns across the subclass property closure.
|
||||
+ final LinkedHashSet<String> aliasSet = new LinkedHashSet<>();
|
||||
+ final LinkedHashSet<String> formulaAliasSet = new LinkedHashSet<>();
|
||||
...
|
||||
|
||||
@@ -699,10 +703,10 @@ public abstract class AbstractEntityPersister
|
||||
if ( selectable instanceof Formula ) {
|
||||
...
|
||||
- if ( prop.isSelectable() && !formulaAliases.contains( formulaAlias ) ) {
|
||||
- formulaAliases.add( formulaAlias );
|
||||
+ if ( prop.isSelectable() ) {
|
||||
+ formulaAliasSet.add( formulaAlias ); // O(1)
|
||||
}
|
||||
}
|
||||
else if ( selectable instanceof Column column ) {
|
||||
...
|
||||
- if ( prop.isSelectable() && !aliases.contains( columnAlias ) ) {
|
||||
- aliases.add( columnAlias );
|
||||
+ if ( prop.isSelectable() ) {
|
||||
+ aliasSet.add( columnAlias ); // O(1)
|
||||
}
|
||||
...
|
||||
}
|
||||
}
|
||||
}
|
||||
- subclassColumnAliasClosure = toStringArray( aliases );
|
||||
- subclassFormulaAliasClosure = toStringArray( formulaAliases );
|
||||
+ subclassColumnAliasClosure = toStringArray( new ArrayList<>( aliasSet ) );
|
||||
+ subclassFormulaAliasClosure = toStringArray( new ArrayList<>( formulaAliasSet ) );
|
||||
```
|
||||
29
defects/hibernate/patch/hibernate-deeper-CLEAN.md
Normal file
29
defects/hibernate/patch/hibernate-deeper-CLEAN.md
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
# Hibernate ORM — Deeper CWE-407 Scan CLEAN Report
|
||||
|
||||
## Scan Date
|
||||
2026-03-27
|
||||
|
||||
## Areas Scanned (beyond hibernate-0001 through hibernate-0006)
|
||||
|
||||
| Area | Files Scanned | Finding |
|
||||
|------|---------------|---------|
|
||||
| `hibernate-core` NaturalIdResolutionsImpl | invalidNaturalIdList.contains() — single lookup per entity, not in outer loop | CLEAN |
|
||||
| `hibernate-core` SemanticQueryBuilder | enumTypes.contains(), JPA_STANDARD_FUNCTIONS.contains() — both are Set<> | CLEAN |
|
||||
| `hibernate-core` BaseSqmToSqlAstConverter | entityNameUsesSet, visitedAssociationKeys — all Set<> types | CLEAN |
|
||||
| `hibernate-core` SqmCteStatement | cteTable.getAttributes().contains() — validation-only, exception path, tiny N | BELOW THRESHOLD |
|
||||
| `hibernate-core` InFlightMetadataCollectorImpl (defaultNamed*) | All HashSet<String> — O(1) contains | CLEAN |
|
||||
| `hibernate-core` InFlightMetadataCollectorImpl (orderedFkSecondPasses) | Already covered by hibernate-0004 | SKIP |
|
||||
| `hibernate-core` AnnotationMetadataSourceProcessorImpl (orderHierarchy) | Already covered by hibernate-0005 | SKIP |
|
||||
| `hibernate-core` sql/Template | All contains() on static final Set fields | CLEAN |
|
||||
| `hibernate-core` sql/ast | All contains() on Set<> typed fields | CLEAN |
|
||||
| `hibernate-core` query/hql/SemanticQueryBuilder | Set<> for all hot lookups | CLEAN |
|
||||
| `hibernate-core` mapping/PersistentClass.containsColumn() | getSelectables().contains() in O(D) declaredProperties loop, called from isDefinedOnMultipleSubclasses() — O(D×S) but subclass-specific, not N² on entity count | BELOW THRESHOLD |
|
||||
| `hibernate-core` mapping/Constraint, ForeignKey, Index | Already covered by hibernate-0001/0002/0003 | SKIP |
|
||||
|
||||
## Confirmed New Defects This Session
|
||||
- **hibernate-0006**: `AbstractEntityPersister` subclass alias closure O(T²) — patched
|
||||
|
||||
## Summary
|
||||
Hibernate ORM scan is comprehensive through hibernate-0006. The remaining contains() calls
|
||||
are on properly-typed Set/HashSet/LinkedHashSet fields or are in low-frequency validation paths
|
||||
with trivially small N. No further HIGH/MEDIUM defects found beyond what has been reported.
|
||||
184
defects/hibernate/unit/HibernateAliasClosureTest.java
Normal file
184
defects/hibernate/unit/HibernateAliasClosureTest.java
Normal file
|
|
@ -0,0 +1,184 @@
|
|||
package unit;
|
||||
|
||||
/**
|
||||
* Regression test for hibernate-0006: CWE-407 O(T²) alias deduplication in
|
||||
* AbstractEntityPersister subclass property closure initialization.
|
||||
*
|
||||
* File: hibernate-core/src/main/java/org/hibernate/persister/entity/AbstractEntityPersister.java
|
||||
* Lines: 665-709
|
||||
*
|
||||
* Defect: aliases and formulaAliases are ArrayList<String>. Inside nested loops over
|
||||
* subclass properties and their selectables, aliases.contains(columnAlias) is O(T)
|
||||
* where T is the growing list of accumulated aliases. For T total selectables, total
|
||||
* cost is O(T²).
|
||||
*
|
||||
* Fix: use LinkedHashSet<String> — O(1) add() handles dedup; insertion order preserved.
|
||||
*/
|
||||
public class HibernateAliasClosureTest {
|
||||
|
||||
// ---- Defective model (mirrors AbstractEntityPersister before fix) ----
|
||||
|
||||
static class DefectivePersisterInit {
|
||||
private final java.util.ArrayList<String> aliases = new java.util.ArrayList<>();
|
||||
private final java.util.ArrayList<String> formulaAliases = new java.util.ArrayList<>();
|
||||
private long ops = 0;
|
||||
|
||||
/**
|
||||
* Simulate processing one selectable column from the subclass property closure.
|
||||
*/
|
||||
void addColumnAlias(String alias) {
|
||||
ops += aliases.size(); // simulate O(N) contains scan
|
||||
if (!aliases.contains(alias)) { // O(N) — CWE-407
|
||||
aliases.add(alias);
|
||||
}
|
||||
}
|
||||
|
||||
void addFormulaAlias(String alias) {
|
||||
ops += formulaAliases.size();
|
||||
if (!formulaAliases.contains(alias)) { // O(N) — CWE-407
|
||||
formulaAliases.add(alias);
|
||||
}
|
||||
}
|
||||
|
||||
java.util.List<String> getColumnAliases() { return aliases; }
|
||||
java.util.List<String> getFormulaAliases() { return formulaAliases; }
|
||||
long getOps() { return ops; }
|
||||
}
|
||||
|
||||
// ---- Fixed model (mirrors AbstractEntityPersister after fix) ----
|
||||
|
||||
static class FixedPersisterInit {
|
||||
private final java.util.LinkedHashSet<String> aliases = new java.util.LinkedHashSet<>();
|
||||
private final java.util.LinkedHashSet<String> formulaAliases = new java.util.LinkedHashSet<>();
|
||||
private long ops = 0;
|
||||
|
||||
void addColumnAlias(String alias) {
|
||||
ops++; // O(1) amortized HashSet.add()
|
||||
aliases.add(alias);
|
||||
}
|
||||
|
||||
void addFormulaAlias(String alias) {
|
||||
ops++;
|
||||
formulaAliases.add(alias);
|
||||
}
|
||||
|
||||
java.util.List<String> getColumnAliases() { return new java.util.ArrayList<>(aliases); }
|
||||
java.util.List<String> getFormulaAliases() { return new java.util.ArrayList<>(formulaAliases); }
|
||||
long getOps() { return ops; }
|
||||
}
|
||||
|
||||
// ---- Tests ----
|
||||
|
||||
public static void main(String[] args) {
|
||||
testDedup();
|
||||
testInsertionOrder();
|
||||
testPerformance();
|
||||
System.out.println("3/3 PASS");
|
||||
}
|
||||
|
||||
static void testDedup() {
|
||||
DefectivePersisterInit defective = new DefectivePersisterInit();
|
||||
FixedPersisterInit fixed = new FixedPersisterInit();
|
||||
|
||||
// Simulate subclass hierarchy: 5 subclasses each contributing 4 columns,
|
||||
// but 2 of those columns are shared (inherited from superclass).
|
||||
// Expected unique aliases: 5 * 2 + 2 = 12
|
||||
for (int sub = 0; sub < 5; sub++) {
|
||||
defective.addColumnAlias("shared_col1_"); // shared — should dedup
|
||||
defective.addColumnAlias("shared_col2_"); // shared — should dedup
|
||||
defective.addColumnAlias("sub" + sub + "_col1_");
|
||||
defective.addColumnAlias("sub" + sub + "_col2_");
|
||||
|
||||
fixed.addColumnAlias("shared_col1_");
|
||||
fixed.addColumnAlias("shared_col2_");
|
||||
fixed.addColumnAlias("sub" + sub + "_col1_");
|
||||
fixed.addColumnAlias("sub" + sub + "_col2_");
|
||||
}
|
||||
|
||||
int expectedUnique = 2 + 5 * 2; // 2 shared + 10 subclass-specific
|
||||
int defectiveCount = defective.getColumnAliases().size();
|
||||
int fixedCount = fixed.getColumnAliases().size();
|
||||
|
||||
if (defectiveCount != expectedUnique) {
|
||||
System.err.println("FAIL testDedup: defective expected " + expectedUnique
|
||||
+ " got " + defectiveCount);
|
||||
System.exit(1);
|
||||
}
|
||||
if (fixedCount != expectedUnique) {
|
||||
System.err.println("FAIL testDedup: fixed expected " + expectedUnique
|
||||
+ " got " + fixedCount);
|
||||
System.exit(1);
|
||||
}
|
||||
System.out.println(" [PASS] alias dedup: expected=" + expectedUnique
|
||||
+ " defective=" + defectiveCount + " fixed=" + fixedCount);
|
||||
}
|
||||
|
||||
static void testInsertionOrder() {
|
||||
// Fixed (LinkedHashSet) must preserve insertion order of first occurrence
|
||||
FixedPersisterInit fixed = new FixedPersisterInit();
|
||||
fixed.addColumnAlias("alpha_");
|
||||
fixed.addColumnAlias("beta_");
|
||||
fixed.addColumnAlias("gamma_");
|
||||
fixed.addColumnAlias("alpha_"); // duplicate — should not affect order
|
||||
fixed.addColumnAlias("beta_"); // duplicate
|
||||
|
||||
java.util.List<String> result = fixed.getColumnAliases();
|
||||
if (result.size() != 3) {
|
||||
System.err.println("FAIL testInsertionOrder: expected 3, got " + result.size());
|
||||
System.exit(1);
|
||||
}
|
||||
if (!result.get(0).equals("alpha_") || !result.get(1).equals("beta_") || !result.get(2).equals("gamma_")) {
|
||||
System.err.println("FAIL testInsertionOrder: wrong order: " + result);
|
||||
System.exit(1);
|
||||
}
|
||||
System.out.println(" [PASS] insertion order preserved: " + result);
|
||||
}
|
||||
|
||||
static void testPerformance() {
|
||||
// Simulate a large TPH hierarchy: 50 subclasses × 20 columns = 1000 selectables,
|
||||
// with the first 5 columns shared (inherited) across all subclasses.
|
||||
final int SUBCLASSES = 50;
|
||||
final int COLS_PER_SUB = 20;
|
||||
final int SHARED_COLS = 5;
|
||||
|
||||
DefectivePersisterInit defective = new DefectivePersisterInit();
|
||||
long t0 = System.nanoTime();
|
||||
for (int sub = 0; sub < SUBCLASSES; sub++) {
|
||||
for (int col = 0; col < SHARED_COLS; col++) {
|
||||
defective.addColumnAlias("shared_" + col + "_");
|
||||
}
|
||||
for (int col = SHARED_COLS; col < COLS_PER_SUB; col++) {
|
||||
defective.addColumnAlias("sub" + sub + "_col" + col + "_");
|
||||
}
|
||||
}
|
||||
long slowTime = System.nanoTime() - t0;
|
||||
long slowOps = defective.getOps();
|
||||
|
||||
FixedPersisterInit fixed = new FixedPersisterInit();
|
||||
t0 = System.nanoTime();
|
||||
for (int sub = 0; sub < SUBCLASSES; sub++) {
|
||||
for (int col = 0; col < SHARED_COLS; col++) {
|
||||
fixed.addColumnAlias("shared_" + col + "_");
|
||||
}
|
||||
for (int col = SHARED_COLS; col < COLS_PER_SUB; col++) {
|
||||
fixed.addColumnAlias("sub" + sub + "_col" + col + "_");
|
||||
}
|
||||
}
|
||||
long fastTime = System.nanoTime() - t0;
|
||||
long fastOps = fixed.getOps();
|
||||
|
||||
double opRatio = fastOps > 0 ? (double) slowOps / fastOps : 1.0;
|
||||
double timeRatio = fastTime > 0 ? (double) slowTime / fastTime : 1.0;
|
||||
|
||||
System.out.printf(" [PERF] subclasses=%d cols=%d total=%d slow_ops=%d fast_ops=%d op_ratio=%.1fx time_ratio=%.1fx%n",
|
||||
SUBCLASSES, COLS_PER_SUB, SUBCLASSES * COLS_PER_SUB,
|
||||
slowOps, fastOps, opRatio, timeRatio);
|
||||
|
||||
if (opRatio < 5.0) {
|
||||
System.err.println("FAIL testPerformance: op ratio " + opRatio + "x < 5x minimum");
|
||||
System.exit(1);
|
||||
}
|
||||
System.out.println(" [PASS] performance: " + String.format("%.1f", opRatio)
|
||||
+ "x op ratio (>= 5x required)");
|
||||
}
|
||||
}
|
||||
24
defects/hypercorn/patch/hypercorn-CLEAN.md
Normal file
24
defects/hypercorn/patch/hypercorn-CLEAN.md
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
# hypercorn — CWE-407 Scan: CLEAN
|
||||
|
||||
**Date:** 2026-03-27
|
||||
**Scope:** Full Python codebase (`src/hypercorn/`)
|
||||
|
||||
## Findings
|
||||
|
||||
No CWE-407 defects confirmed.
|
||||
|
||||
### Patterns examined
|
||||
|
||||
| Location | Pattern | Verdict |
|
||||
|----------|---------|---------|
|
||||
| `hypercorn/utils.py:filter_pseudo_headers` | single-pass over headers list | CLEAN — O(H), no inner scan |
|
||||
| `hypercorn/utils.py:build_and_validate_headers` | single-pass over headers list | CLEAN — O(H), no inner scan |
|
||||
| `hypercorn/protocol/http_stream.py:160,206,232` | `for name, value in scope["headers"]` with `break` | CLEAN — O(H) early-exit scans, not nested |
|
||||
| `hypercorn/protocol/h2.py` | HTTP/2 stream management via `dict[int, stream]` | CLEAN — dict-keyed by stream_id, O(1) lookup |
|
||||
| `hypercorn/middleware/dispatcher.py` | `for path, app in self.mounts.items()` | CLEAN — mount table is small and bounded |
|
||||
| `hypercorn/config.py:response_headers` | `for alt_svc_header in self.alt_svc_headers` | CLEAN — alt_svc_headers bounded (1-2 entries typical) |
|
||||
|
||||
## Conclusion
|
||||
|
||||
Hypercorn uses single-pass header iterations throughout and relies on dicts
|
||||
for stream management. No quadratic patterns found.
|
||||
51
defects/linux/patch/linux-deeper-CLEAN.md
Normal file
51
defects/linux/patch/linux-deeper-CLEAN.md
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
# linux: CWE-407 deeper scan — net/core, kernel/ CLEAN
|
||||
|
||||
## Subsystems scanned
|
||||
|
||||
| Subsystem | Files | Verdict |
|
||||
|-----------|-------|---------|
|
||||
| `net/core/dev.c` | for_each_netdev, adjacency lists | CLEAN |
|
||||
| `net/core/fib_rules.c` | rule_find, rule_exists, list_for_each_entry | CLEAN |
|
||||
| `net/core/sock.c` | proto_register, assign_proto_idx | CLEAN |
|
||||
| `kernel/groups.c` | groups_search, supplementary GIDs | CLEAN |
|
||||
| `kernel/user_namespace.c` | mappings_overlap, uid/gid extents | BOUNDED |
|
||||
| `kernel/sched/topology.c` | find_pd, for_each_cpu perf domains | CLEAN |
|
||||
| `kernel/sched/core.c` | for_each_cpu + smt_mask, cpumask_andnot | CLEAN |
|
||||
| `kernel/workqueue.c` | for_each_pwq, for_each_pool | CLEAN |
|
||||
| `kernel/notifier.c` | raw_notifier_call_chain | CLEAN |
|
||||
|
||||
## Notes
|
||||
|
||||
### net/core/dev.c — `__dev_alloc_name`
|
||||
Outer `for_each_netdev` with inner `netdev_for_each_altname` scans a bitmap of
|
||||
used slots — sets a bit per slot using `bitmap_zalloc(max_netdevices)`.
|
||||
Not a membership test inside a growing list; the bitmap is reset fresh each call.
|
||||
CLEAN.
|
||||
|
||||
### net/core/fib_rules.c — `rule_find`, `rule_exists`
|
||||
Both do a single sequential pass over `ops->rules_list`. The three sequential
|
||||
passes in `fib_nl_newrule` (ctarget scan, pref-order scan, unresolved-rules
|
||||
update) are not nested — each is O(R) standalone. CLEAN.
|
||||
|
||||
### kernel/user_namespace.c — `mappings_overlap`
|
||||
Called in outer loop over lines in the UID/GID map file; inner loop checks
|
||||
overlap against already-accepted extents. **Technically O(E²)** but bounded by
|
||||
`UID_GID_MAP_MAX_EXTENTS = 340`. Peak: 340² = 115,600 comparisons on one
|
||||
`write()` call. Not network-facing; attacker must have a user namespace.
|
||||
Below threshold for a new defect ticket; noted here for completeness.
|
||||
|
||||
### kernel/sched/topology.c — `find_pd`
|
||||
`find_pd()` walks a short linked list of `perf_domain` objects (O(P), where P =
|
||||
number of distinct CPU performance domains — typically 1–4 on real hardware)
|
||||
inside `for_each_cpu(i, cpu_map)`. Worst case O(C×P) but P is structurally
|
||||
bounded by hardware topology, not attacker-controlled. CLEAN.
|
||||
|
||||
### net/ipv4/, fs/, mm/ — NOT IN SPARSE CLONE
|
||||
The sparse clone does not include `net/ipv4/`, `fs/`, or `mm/`. Those
|
||||
subsystems require a fresh clone or broader sparse-checkout configuration.
|
||||
|
||||
## Conclusion
|
||||
|
||||
No new CWE-407 defects found in the scanned kernel subsystems beyond
|
||||
linux-0001..0008. The sparse clone limits further scanning of `net/ipv4/`,
|
||||
`fs/`, and `mm/`.
|
||||
147
defects/openssl/patch/openssl-0004-store-cert-subjects-lhash.md
Normal file
147
defects/openssl/patch/openssl-0004-store-cert-subjects-lhash.md
Normal file
|
|
@ -0,0 +1,147 @@
|
|||
# openssl-0004: CWE-407 O(N²) CA name dedup in SSL_add_store_cert_subjects_to_stack
|
||||
|
||||
## Severity: MEDIUM
|
||||
|
||||
## Location
|
||||
`ssl/ssl_cert.c` — `add_uris_recursive()` called from `SSL_add_store_cert_subjects_to_stack()`
|
||||
|
||||
## Description
|
||||
|
||||
`SSL_add_store_cert_subjects_to_stack()` loads CA subject names from a URI store
|
||||
(e.g., a PKCS#11 token or directory) and deduplicates them into a
|
||||
`STACK_OF(X509_NAME)`. It calls `add_uris_recursive()` for the actual loading.
|
||||
|
||||
`add_uris_recursive()` contains this pattern:
|
||||
|
||||
```c
|
||||
while (!OSSL_STORE_eof(ctx) && !OSSL_STORE_error(ctx)) {
|
||||
...
|
||||
if (sk_X509_NAME_find(stack, xn) >= 0) { /* O(N) linear scan */
|
||||
/* Duplicate */
|
||||
X509_NAME_free(xn);
|
||||
} else if (!sk_X509_NAME_push(stack, xn)) {
|
||||
...
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
`sk_X509_NAME_find()` calls `internal_find()`. When the stack is **unsorted**
|
||||
(which it is here — `add_uris_recursive` never calls `sk_X509_NAME_sort()`),
|
||||
`internal_find()` falls through to a linear scan:
|
||||
|
||||
```c
|
||||
if (!st->sorted) {
|
||||
for (i = 0; i < st->num; i++) {
|
||||
cmp_ret = cmp_with_thunk(st, &data, st->data + i);
|
||||
if (cmp_ret == 0) { ... return i; }
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
```
|
||||
|
||||
Each call is O(N). Loading N certificates from the store requires N such calls,
|
||||
giving **O(N²)** total comparisons.
|
||||
|
||||
By contrast, both `SSL_add_file_cert_subjects_to_stack` and
|
||||
`SSL_add_dir_cert_subjects_to_stack` received an explicit LHASH fix (visible in
|
||||
the same file) that pre-populates an `LHASH_OF(X509_NAME)` for O(1) duplicate
|
||||
detection. `SSL_add_store_cert_subjects_to_stack` was left behind.
|
||||
|
||||
## Complexity Before Fix
|
||||
|
||||
| N (CA certs in store) | Comparisons (O(N²)) |
|
||||
|-----------------------|---------------------|
|
||||
| 100 | ~5,050 |
|
||||
| 500 | ~125,250 |
|
||||
| 1,000 | ~500,500 |
|
||||
| 5,000 | ~12,502,500 |
|
||||
|
||||
Each comparison calls `xname_sk_cmp → X509_NAME_cmp → X509_NAME_cmp_ex`
|
||||
which allocates and DER-encodes the name — making each O(N) scan carry
|
||||
non-trivial constant work beyond the raw count.
|
||||
|
||||
## Fix
|
||||
|
||||
Pass an `LHASH_OF(X509_NAME)` into `add_uris_recursive()` just as
|
||||
`SSL_add_dir_cert_subjects_to_stack` does for the per-file helper, replacing
|
||||
`sk_X509_NAME_find()` with `lh_X509_NAME_retrieve()`.
|
||||
|
||||
```diff
|
||||
--- a/ssl/ssl_cert.c
|
||||
+++ b/ssl/ssl_cert.c
|
||||
@@ -1022,7 +1022,8 @@ static int add_uris_recursive(STACK_OF(X509_NAME) *stack,
|
||||
-static int add_uris_recursive(STACK_OF(X509_NAME) *stack,
|
||||
- const char *uri, int depth)
|
||||
+static int add_uris_recursive(STACK_OF(X509_NAME) *stack,
|
||||
+ LHASH_OF(X509_NAME) *name_hash,
|
||||
+ const char *uri, int depth)
|
||||
{
|
||||
...
|
||||
if (infotype == OSSL_STORE_INFO_NAME) {
|
||||
if (depth > 0)
|
||||
- ok = add_uris_recursive(stack, OSSL_STORE_INFO_get0_NAME(info),
|
||||
- depth - 1);
|
||||
+ ok = add_uris_recursive(stack, name_hash,
|
||||
+ OSSL_STORE_INFO_get0_NAME(info), depth - 1);
|
||||
} else if (infotype == OSSL_STORE_INFO_CERT) {
|
||||
...
|
||||
- if (sk_X509_NAME_find(stack, xn) >= 0) {
|
||||
+ if (lh_X509_NAME_retrieve(name_hash, xn) != NULL) {
|
||||
/* Duplicate. */
|
||||
X509_NAME_free(xn);
|
||||
} else if (!sk_X509_NAME_push(stack, xn)) {
|
||||
...
|
||||
+ } else {
|
||||
+ lh_X509_NAME_insert(name_hash, xn);
|
||||
}
|
||||
}
|
||||
...
|
||||
}
|
||||
|
||||
int SSL_add_store_cert_subjects_to_stack(STACK_OF(X509_NAME) *stack,
|
||||
const char *store)
|
||||
{
|
||||
int (*oldcmp)(const X509_NAME *const *a, const X509_NAME *const *b)
|
||||
= sk_X509_NAME_set_cmp_func(stack, xname_sk_cmp);
|
||||
+ LHASH_OF(X509_NAME) *name_hash = lh_X509_NAME_new(xname_hash, xname_cmp);
|
||||
+ int ret;
|
||||
+ X509_NAME *xn;
|
||||
+ int idx, num;
|
||||
+
|
||||
+ if (name_hash == NULL) {
|
||||
+ (void)sk_X509_NAME_set_cmp_func(stack, oldcmp);
|
||||
+ return 0;
|
||||
+ }
|
||||
+
|
||||
+ /* Pre-populate lhash with names already on the stack */
|
||||
+ num = sk_X509_NAME_num(stack);
|
||||
+ for (idx = 0; idx < num; idx++) {
|
||||
+ xn = sk_X509_NAME_value(stack, idx);
|
||||
+ lh_X509_NAME_insert(name_hash, xn);
|
||||
+ }
|
||||
+
|
||||
- int ret = add_uris_recursive(stack, store, 1);
|
||||
+ ret = add_uris_recursive(stack, name_hash, store, 1);
|
||||
+ lh_X509_NAME_free(name_hash);
|
||||
(void)sk_X509_NAME_set_cmp_func(stack, oldcmp);
|
||||
return ret;
|
||||
}
|
||||
```
|
||||
|
||||
## Complexity After Fix
|
||||
|
||||
O(N) total — each name is hashed once (O(1) amortized) during insertion.
|
||||
`lh_X509_NAME_retrieve` is O(1) amortized.
|
||||
|
||||
## Speedup
|
||||
|
||||
| N | Before (comparisons) | After (hash ops) | Ratio |
|
||||
|-----|---------------------|------------------|-------|
|
||||
| 100 | 5,050 | ~100 | ~50x |
|
||||
| 500 | 125,250 | ~500 | ~250x |
|
||||
| 1000| 500,500 | ~1,000 | ~500x |
|
||||
|
||||
## References
|
||||
- CWE-407: Inefficient Algorithmic Complexity
|
||||
- `ssl/ssl_cert.c` — `SSL_add_dir_cert_subjects_to_stack()` (already fixed, same file)
|
||||
- `crypto/stack/stack.c` — `internal_find()` linear path when `!st->sorted`
|
||||
194
defects/openssl/unit/OpenSslStoreCertSubjectsTest.java
Normal file
194
defects/openssl/unit/OpenSslStoreCertSubjectsTest.java
Normal file
|
|
@ -0,0 +1,194 @@
|
|||
package unit;
|
||||
|
||||
/**
|
||||
* openssl-0004 unit test
|
||||
*
|
||||
* Models the O(N²) CA subject name dedup in add_uris_recursive()
|
||||
* (called from SSL_add_store_cert_subjects_to_stack) and the O(N) fixed
|
||||
* version that uses a hash set.
|
||||
*
|
||||
* The defect: sk_X509_NAME_find() on an unsorted stack is O(N) linear scan.
|
||||
* Loading N certs from a store calls it N times → O(N²).
|
||||
* The existing SSL_add_dir/file functions already use LHASH for O(1) dedup;
|
||||
* add_uris_recursive was left behind.
|
||||
*
|
||||
* Compile: javac -d . OpenSslStoreCertSubjectsTest.java
|
||||
* Run: java unit.OpenSslStoreCertSubjectsTest
|
||||
*/
|
||||
public class OpenSslStoreCertSubjectsTest {
|
||||
|
||||
// ------------------------------------------------------------------ //
|
||||
// DEFECTIVE: O(N²) — linear scan for each new cert (unsorted stack) //
|
||||
// ------------------------------------------------------------------ //
|
||||
|
||||
/** Models an X509_NAME as a unique integer DN string. */
|
||||
static final class Name {
|
||||
final String dn;
|
||||
Name(String dn) { this.dn = dn; }
|
||||
@Override public boolean equals(Object o) {
|
||||
return o instanceof Name && ((Name) o).dn.equals(dn);
|
||||
}
|
||||
@Override public int hashCode() { return dn.hashCode(); }
|
||||
}
|
||||
|
||||
/**
|
||||
* SLOW — simulates add_uris_recursive with sk_X509_NAME_find (linear).
|
||||
* Returns number of comparison operations performed.
|
||||
*/
|
||||
static long defectiveAddToStack(Name[] incoming, java.util.List<Name> stack) {
|
||||
long ops = 0;
|
||||
for (Name xn : incoming) {
|
||||
// sk_X509_NAME_find: linear scan when stack is unsorted
|
||||
boolean found = false;
|
||||
for (Name existing : stack) {
|
||||
ops++;
|
||||
if (existing.equals(xn)) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
stack.add(xn);
|
||||
}
|
||||
}
|
||||
return ops;
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------ //
|
||||
// FIXED: O(N) — hash set for O(1) dedup (mirrors LHASH_OF fix) //
|
||||
// ------------------------------------------------------------------ //
|
||||
|
||||
/**
|
||||
* FAST — simulates add_uris_recursive with lh_X509_NAME_retrieve (hash).
|
||||
* Returns number of hash operations performed.
|
||||
*/
|
||||
static long fixedAddToStack(Name[] incoming, java.util.List<Name> stack) {
|
||||
long ops = 0;
|
||||
java.util.HashSet<Name> nameHash = new java.util.HashSet<>(stack);
|
||||
ops += stack.size(); // pre-populate cost (O(N_existing))
|
||||
for (Name xn : incoming) {
|
||||
ops++;
|
||||
if (!nameHash.contains(xn)) {
|
||||
stack.add(xn);
|
||||
nameHash.add(xn);
|
||||
}
|
||||
}
|
||||
return ops;
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------ //
|
||||
// Tests //
|
||||
// ------------------------------------------------------------------ //
|
||||
static int pass = 0, fail = 0;
|
||||
|
||||
static void check(String name, boolean cond) {
|
||||
if (cond) {
|
||||
System.out.println(" PASS " + name);
|
||||
pass++;
|
||||
} else {
|
||||
System.out.println(" FAIL " + name);
|
||||
fail++;
|
||||
}
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
System.out.println("=== openssl-0004: SSL_add_store_cert_subjects O(N²) defect ===\n");
|
||||
|
||||
// --- Correctness tests ---
|
||||
|
||||
// Test 1: dedup - distinct names all added
|
||||
{
|
||||
Name[] certs = {new Name("CN=A"), new Name("CN=B"), new Name("CN=C")};
|
||||
java.util.List<Name> slow = new java.util.ArrayList<>();
|
||||
java.util.List<Name> fast = new java.util.ArrayList<>();
|
||||
defectiveAddToStack(certs, slow);
|
||||
fixedAddToStack(certs, fast);
|
||||
check("all distinct names added (slow)", slow.size() == 3);
|
||||
check("all distinct names added (fast)", fast.size() == 3);
|
||||
}
|
||||
|
||||
// Test 2: duplicates are suppressed
|
||||
{
|
||||
Name a1 = new Name("CN=A");
|
||||
Name a2 = new Name("CN=A"); // same DN, different object
|
||||
Name b = new Name("CN=B");
|
||||
Name[] certs = {a1, a2, b, a1};
|
||||
java.util.List<Name> slow = new java.util.ArrayList<>();
|
||||
java.util.List<Name> fast = new java.util.ArrayList<>();
|
||||
defectiveAddToStack(certs, slow);
|
||||
fixedAddToStack(certs, fast);
|
||||
check("duplicates suppressed (slow)", slow.size() == 2);
|
||||
check("duplicates suppressed (fast)", fast.size() == 2);
|
||||
}
|
||||
|
||||
// Test 3: pre-existing stack entries are not duplicated
|
||||
{
|
||||
Name pre = new Name("CN=Pre");
|
||||
Name[] certs = {new Name("CN=Pre"), new Name("CN=New")};
|
||||
java.util.List<Name> slow = new java.util.ArrayList<>();
|
||||
java.util.List<Name> fast = new java.util.ArrayList<>();
|
||||
slow.add(pre);
|
||||
fast.add(pre);
|
||||
defectiveAddToStack(certs, slow);
|
||||
fixedAddToStack(certs, fast);
|
||||
check("pre-existing not duplicated (slow)", slow.size() == 2);
|
||||
check("pre-existing not duplicated (fast)", fast.size() == 2);
|
||||
}
|
||||
|
||||
// Test 4: empty store — no names added
|
||||
{
|
||||
Name[] empty = {};
|
||||
java.util.List<Name> slow = new java.util.ArrayList<>();
|
||||
java.util.List<Name> fast = new java.util.ArrayList<>();
|
||||
defectiveAddToStack(empty, slow);
|
||||
fixedAddToStack(empty, fast);
|
||||
check("empty store → empty stack (slow)", slow.size() == 0);
|
||||
check("empty store → empty stack (fast)", fast.size() == 0);
|
||||
}
|
||||
|
||||
// Test 5: single cert, no existing stack — added once
|
||||
{
|
||||
Name[] certs = {new Name("CN=Solo")};
|
||||
java.util.List<Name> slow = new java.util.ArrayList<>();
|
||||
java.util.List<Name> fast = new java.util.ArrayList<>();
|
||||
defectiveAddToStack(certs, slow);
|
||||
fixedAddToStack(certs, fast);
|
||||
check("single cert added (slow)", slow.size() == 1);
|
||||
check("single cert added (fast)", fast.size() == 1);
|
||||
}
|
||||
|
||||
// --- Complexity comparison ---
|
||||
System.out.println();
|
||||
|
||||
int[] sizes = {100, 500, 1000};
|
||||
for (int N : sizes) {
|
||||
// N unique certs + N duplicate certs (every cert appears twice)
|
||||
Name[] incoming = new Name[N * 2];
|
||||
for (int i = 0; i < N; i++) {
|
||||
incoming[i] = new Name("CN=Issuer-" + i);
|
||||
incoming[N + i] = new Name("CN=Issuer-" + i); // duplicate
|
||||
}
|
||||
|
||||
java.util.List<Name> slowStack = new java.util.ArrayList<>();
|
||||
java.util.List<Name> fastStack = new java.util.ArrayList<>();
|
||||
|
||||
long slowOps = defectiveAddToStack(incoming, slowStack);
|
||||
long fastOps = fixedAddToStack(incoming, fastStack);
|
||||
|
||||
double ratio = (double) slowOps / Math.max(fastOps, 1);
|
||||
|
||||
System.out.printf(
|
||||
" N=%4d slow=%7d ops fast=%5d ops ratio=%.1fx%n",
|
||||
N, slowOps, fastOps, ratio);
|
||||
|
||||
check("N=" + N + ": both stacks have " + N + " names",
|
||||
slowStack.size() == N && fastStack.size() == N);
|
||||
check("N=" + N + ": slow uses at least 5x more ops than fast",
|
||||
ratio >= 5.0);
|
||||
}
|
||||
|
||||
System.out.println("\n--- " + (pass + fail) + " tests: " + pass
|
||||
+ " passed, " + fail + " failed ---");
|
||||
if (fail > 0) System.exit(1);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,103 @@
|
|||
# UNDF: UNDF-2026-000000400
|
||||
# spring-0006: VersionResourceResolver — O(N²) patternsList.contains() in addFixedVersionStrategy()
|
||||
|
||||
## CWE-407 — Algorithmic Complexity: Linear Membership Test in Loop
|
||||
|
||||
| Field | Value |
|
||||
|--------------|-------|
|
||||
| ID | spring-0006 |
|
||||
| Severity | MEDIUM |
|
||||
| Ecosystem | spring-framework |
|
||||
| Package | spring-webmvc |
|
||||
| File | `spring-webmvc/src/main/java/org/springframework/web/servlet/resource/VersionResourceResolver.java` |
|
||||
| Lines | 130–141 |
|
||||
| Complexity | O(N²) where N = pathPatterns.length |
|
||||
| Fix | Convert `patternsList` to `HashSet<String>` for O(1) membership check |
|
||||
|
||||
## Defective Code
|
||||
|
||||
```java
|
||||
// Line 130-141: VersionResourceResolver.addFixedVersionStrategy()
|
||||
public VersionResourceResolver addFixedVersionStrategy(String version, String... pathPatterns) {
|
||||
List<String> patternsList = Arrays.asList(pathPatterns); // O(N) list
|
||||
List<String> prefixedPatterns = new ArrayList<>(pathPatterns.length);
|
||||
String versionPrefix = "/" + version;
|
||||
for (String pattern : patternsList) { // outer loop: O(N)
|
||||
prefixedPatterns.add(pattern);
|
||||
if (!pattern.startsWith(versionPrefix) && !patternsList.contains(versionPrefix + pattern)) {
|
||||
// ^^^^^^^^^^^^^^^^^^^^ O(N) scan — CWE-407
|
||||
prefixedPatterns.add(versionPrefix + pattern);
|
||||
}
|
||||
}
|
||||
return addVersionStrategy(new FixedVersionStrategy(version), StringUtils.toStringArray(prefixedPatterns));
|
||||
}
|
||||
```
|
||||
|
||||
**Pattern:** `for (x : list) { list.contains(y) }` — inner `contains()` is O(N) on
|
||||
`Arrays.asList()` backed array. Outer loop is O(N). Total: **O(N²)**.
|
||||
|
||||
Triggered at application startup / configuration time when configuring versioned resource
|
||||
resolvers (common in Spring MVC static resource handling). Large Spring apps with many
|
||||
path patterns suffer quadratic cost.
|
||||
|
||||
## Complexity Table
|
||||
|
||||
| N (pathPatterns) | Operations (before) | Operations (after) |
|
||||
|-----------------|---------------------|-------------------|
|
||||
| 10 | ~100 | ~10 |
|
||||
| 100 | ~10,000 | ~100 |
|
||||
| 1,000 | ~1,000,000 | ~1,000 |
|
||||
|
||||
Speedup ratio: **~N×** — 100x at N=100, 1000x at N=1000.
|
||||
|
||||
## Fix
|
||||
|
||||
```java
|
||||
public VersionResourceResolver addFixedVersionStrategy(String version, String... pathPatterns) {
|
||||
// spring-0006 fix: HashSet for O(1) membership check.
|
||||
// Previously Arrays.asList() returned a plain List — patternsList.contains() was O(N).
|
||||
// With N patterns, the loop called contains() N times = O(N²) total.
|
||||
Set<String> patternsSet = new HashSet<>(Arrays.asList(pathPatterns));
|
||||
List<String> prefixedPatterns = new ArrayList<>(pathPatterns.length * 2);
|
||||
String versionPrefix = "/" + version;
|
||||
for (String pattern : pathPatterns) {
|
||||
prefixedPatterns.add(pattern);
|
||||
if (!pattern.startsWith(versionPrefix) && !patternsSet.contains(versionPrefix + pattern)) {
|
||||
prefixedPatterns.add(versionPrefix + pattern);
|
||||
}
|
||||
}
|
||||
return addVersionStrategy(new FixedVersionStrategy(version), StringUtils.toStringArray(prefixedPatterns));
|
||||
}
|
||||
```
|
||||
|
||||
## Patch
|
||||
|
||||
```diff
|
||||
--- a/spring-webmvc/src/main/java/org/springframework/web/servlet/resource/VersionResourceResolver.java
|
||||
+++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/resource/VersionResourceResolver.java
|
||||
@@ -27,6 +27,7 @@ import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Comparator;
|
||||
+import java.util.HashSet;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
+import java.util.Set;
|
||||
|
||||
@@ -130,10 +131,13 @@ public class VersionResourceResolver extends AbstractResourceResolver {
|
||||
public VersionResourceResolver addFixedVersionStrategy(String version, String... pathPatterns) {
|
||||
- List<String> patternsList = Arrays.asList(pathPatterns);
|
||||
- List<String> prefixedPatterns = new ArrayList<>(pathPatterns.length);
|
||||
+ // spring-0006 fix: use HashSet for O(1) duplicate detection; was O(N²) with List.contains()
|
||||
+ Set<String> patternsSet = new HashSet<>(Arrays.asList(pathPatterns));
|
||||
+ List<String> prefixedPatterns = new ArrayList<>(pathPatterns.length * 2);
|
||||
String versionPrefix = "/" + version;
|
||||
- for (String pattern : patternsList) {
|
||||
+ for (String pattern : pathPatterns) {
|
||||
prefixedPatterns.add(pattern);
|
||||
- if (!pattern.startsWith(versionPrefix) && !patternsList.contains(versionPrefix + pattern)) {
|
||||
+ if (!pattern.startsWith(versionPrefix) && !patternsSet.contains(versionPrefix + pattern)) {
|
||||
prefixedPatterns.add(versionPrefix + pattern);
|
||||
}
|
||||
}
|
||||
```
|
||||
27
defects/spring/patch/spring-deeper-CLEAN.md
Normal file
27
defects/spring/patch/spring-deeper-CLEAN.md
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
# Spring Framework — Deeper CWE-407 Scan CLEAN Report
|
||||
|
||||
## Scan Date
|
||||
2026-03-27
|
||||
|
||||
## Areas Scanned (beyond spring-0001 through spring-0006)
|
||||
|
||||
| Area | Files Scanned | Finding |
|
||||
|------|---------------|---------|
|
||||
| `spring-aop` AdvisedSupport.addInterface() | ArrayList.contains() called once per proxy interface — not in outer loop | CLEAN (low-frequency config) |
|
||||
| `spring-webmvc` ContentNegotiatingViewResolver | matchingBeans.contains(vr) in O(V) loop — Collection from spring bean factory (backed by LinkedHashMap.values()), O(V) each = O(V²) init-only | BELOW THRESHOLD |
|
||||
| `spring-webmvc` ResourceHandlerRegistry.hasMappingForPattern() | Arrays.asList(array).contains() in O(R) loop over registrations | BELOW THRESHOLD (config-time, early return) |
|
||||
| `spring-web` CORS processing (CorsConfiguration) | All contains() calls on static constant sets (Set.of) or single lookups | CLEAN |
|
||||
| `spring-web` HttpExchangeBeanRegistrationAotProcessor | exchangeInterfaces.contains() inside method iteration — AOT-time only, I×M×I | BELOW THRESHOLD (AOT only) |
|
||||
| `spring-core` PathMatchingResourcePatternResolver | result.contains() — result is LinkedHashSet | CLEAN |
|
||||
| `spring-core` annotation AnnotationTypeMapping | Already covered by spring-0005 |SKIP |
|
||||
| `spring-context` AbstractApplicationEventMulticaster | Already covered by spring-0003/0004 | SKIP |
|
||||
| `spring-beans` | No ArrayList.contains() in loops found | CLEAN |
|
||||
|
||||
## Confirmed New Defects This Session
|
||||
- **spring-0006**: `VersionResourceResolver.addFixedVersionStrategy()` O(N²) — patched
|
||||
|
||||
## Summary
|
||||
Spring framework scan is comprehensive through spring-0006. The remaining contains() calls
|
||||
are either on constant-initialized Sets, AOT/init-time only with small N, or already patched.
|
||||
No further HIGH/MEDIUM defects found in spring-aop, spring-beans, spring-web, or spring-webmvc
|
||||
beyond what has been reported.
|
||||
134
defects/spring/unit/SpringVersionResourceResolverTest.java
Normal file
134
defects/spring/unit/SpringVersionResourceResolverTest.java
Normal file
|
|
@ -0,0 +1,134 @@
|
|||
package unit;
|
||||
|
||||
/**
|
||||
* Regression test for spring-0006: CWE-407 O(N²) patternsList.contains() inside
|
||||
* addFixedVersionStrategy() loop in VersionResourceResolver.
|
||||
*
|
||||
* File: spring-webmvc/src/main/java/org/springframework/web/servlet/resource/VersionResourceResolver.java
|
||||
* Lines: 130-141
|
||||
*
|
||||
* Defect: patternsList = Arrays.asList(pathPatterns) then for each pattern calls
|
||||
* patternsList.contains(versionPrefix + pattern) — O(N) inside O(N) loop = O(N²).
|
||||
*
|
||||
* Fix: use HashSet<String> for O(1) membership check.
|
||||
*/
|
||||
public class SpringVersionResourceResolverTest {
|
||||
|
||||
// ---- Defective model (mirrors VersionResourceResolver before fix) ----
|
||||
|
||||
static java.util.List<String> addFixedVersionStrategyDefective(String version, String... pathPatterns) {
|
||||
java.util.List<String> patternsList = java.util.Arrays.asList(pathPatterns); // plain List — O(N) contains
|
||||
java.util.List<String> prefixedPatterns = new java.util.ArrayList<>(pathPatterns.length);
|
||||
String versionPrefix = "/" + version;
|
||||
long ops = 0;
|
||||
for (String pattern : patternsList) {
|
||||
prefixedPatterns.add(pattern);
|
||||
ops++;
|
||||
for (String p : patternsList) { ops++; } // simulate contains() scan
|
||||
if (!pattern.startsWith(versionPrefix) && !patternsList.contains(versionPrefix + pattern)) {
|
||||
prefixedPatterns.add(versionPrefix + pattern);
|
||||
}
|
||||
}
|
||||
slowOps = ops;
|
||||
return prefixedPatterns;
|
||||
}
|
||||
|
||||
// ---- Fixed model (mirrors VersionResourceResolver after fix) ----
|
||||
|
||||
static java.util.List<String> addFixedVersionStrategyFixed(String version, String... pathPatterns) {
|
||||
java.util.Set<String> patternsSet = new java.util.HashSet<>(java.util.Arrays.asList(pathPatterns));
|
||||
java.util.List<String> prefixedPatterns = new java.util.ArrayList<>(pathPatterns.length * 2);
|
||||
String versionPrefix = "/" + version;
|
||||
long ops = 0;
|
||||
for (String pattern : pathPatterns) {
|
||||
prefixedPatterns.add(pattern);
|
||||
ops++;
|
||||
ops++; // HashSet.contains() = O(1)
|
||||
if (!pattern.startsWith(versionPrefix) && !patternsSet.contains(versionPrefix + pattern)) {
|
||||
prefixedPatterns.add(versionPrefix + pattern);
|
||||
}
|
||||
}
|
||||
fastOps = ops;
|
||||
return prefixedPatterns;
|
||||
}
|
||||
|
||||
static long slowOps = 0;
|
||||
static long fastOps = 0;
|
||||
|
||||
// ---- Tests ----
|
||||
|
||||
public static void main(String[] args) {
|
||||
testCorrectness();
|
||||
testNoFalseDuplicates();
|
||||
testPerformance();
|
||||
System.out.println("2/2 PASS");
|
||||
}
|
||||
|
||||
static void testCorrectness() {
|
||||
// A version prefix already in the patterns list should NOT be duplicated
|
||||
java.util.List<String> result = addFixedVersionStrategyFixed("1.0.0",
|
||||
"/js/**", "/css/**", "/1.0.0/js/**");
|
||||
|
||||
// "/js/**" already has versioned form in list — should not add "/1.0.0/js/**" again
|
||||
long count = result.stream().filter(p -> p.equals("/1.0.0/js/**")).count();
|
||||
if (count != 1) {
|
||||
System.err.println("FAIL testCorrectness: expected 1 occurrence of /1.0.0/js/**, got " + count);
|
||||
System.exit(1);
|
||||
}
|
||||
|
||||
// "/css/**" has no versioned form yet — should add "/1.0.0/css/**"
|
||||
count = result.stream().filter(p -> p.equals("/1.0.0/css/**")).count();
|
||||
if (count != 1) {
|
||||
System.err.println("FAIL testCorrectness: expected 1 occurrence of /1.0.0/css/**, got " + count);
|
||||
System.exit(1);
|
||||
}
|
||||
System.out.println(" [PASS] correctness: versioned patterns deduped correctly");
|
||||
}
|
||||
|
||||
static void testNoFalseDuplicates() {
|
||||
// Both implementations should produce same result
|
||||
String[] patterns = {"/a/**", "/b/**", "/c/**", "/1.5/a/**"};
|
||||
java.util.List<String> defective = addFixedVersionStrategyDefective("1.5", patterns);
|
||||
java.util.List<String> fixed = addFixedVersionStrategyFixed("1.5", patterns);
|
||||
|
||||
if (defective.size() != fixed.size()) {
|
||||
System.err.println("FAIL testNoFalseDuplicates: defective=" + defective.size()
|
||||
+ " fixed=" + fixed.size() + " — results differ");
|
||||
System.exit(1);
|
||||
}
|
||||
System.out.println(" [PASS] no false duplicates: defective=" + defective.size()
|
||||
+ " fixed=" + fixed.size());
|
||||
}
|
||||
|
||||
static void testPerformance() {
|
||||
final int N = 2000;
|
||||
String[] patterns = new String[N];
|
||||
for (int i = 0; i < N; i++) {
|
||||
patterns[i] = "/path" + i + "/**";
|
||||
}
|
||||
|
||||
// Defective: O(N²) operations
|
||||
long t0 = System.nanoTime();
|
||||
addFixedVersionStrategyDefective("v1.0", patterns);
|
||||
long slowTime = System.nanoTime() - t0;
|
||||
long slowCount = slowOps;
|
||||
|
||||
// Fixed: O(N) operations
|
||||
t0 = System.nanoTime();
|
||||
addFixedVersionStrategyFixed("v1.0", patterns);
|
||||
long fastTime = System.nanoTime() - t0;
|
||||
long fastCount = fastOps;
|
||||
|
||||
double opRatio = (double) slowCount / fastCount;
|
||||
double timeRatio = slowTime > 0 ? (double) slowTime / fastTime : 1.0;
|
||||
|
||||
System.out.printf(" [PERF] N=%d slow_ops=%d fast_ops=%d op_ratio=%.1fx time_ratio=%.1fx%n",
|
||||
N, slowCount, fastCount, opRatio, timeRatio);
|
||||
|
||||
if (opRatio < 5.0) {
|
||||
System.err.println("FAIL testPerformance: op ratio " + opRatio + "x < 5x minimum");
|
||||
System.exit(1);
|
||||
}
|
||||
System.out.println(" [PASS] performance: " + opRatio + "x op ratio (>= 5x required)");
|
||||
}
|
||||
}
|
||||
23
defects/uvicorn/patch/uvicorn-CLEAN.md
Normal file
23
defects/uvicorn/patch/uvicorn-CLEAN.md
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
# uvicorn — CWE-407 Scan: CLEAN
|
||||
|
||||
**Date:** 2026-03-27
|
||||
**Scope:** Full Python codebase (`uvicorn/`)
|
||||
|
||||
## Findings
|
||||
|
||||
No CWE-407 defects confirmed.
|
||||
|
||||
### Patterns examined
|
||||
|
||||
| Location | Pattern | Verdict |
|
||||
|----------|---------|---------|
|
||||
| `uvicorn/middleware/proxy_headers.py:_TrustedHosts` | trusted host lookup | CLEAN — uses `set[ipaddress.IPv4Address]`, `set[ipaddress.IPv6Address]`, and `set[str]` for O(1) membership |
|
||||
| `uvicorn/protocols/http/httptools_impl.py:479` | `CLOSE_HEADER in headers` (list) | NOT O(n²) — single linear scan once per response, not in an outer loop |
|
||||
| `uvicorn/protocols/http/h11_impl.py:476` | `CLOSE_HEADER in headers` (list) | NOT O(n²) — same as above |
|
||||
| `uvicorn/protocols/http/httptools_impl.py:145` | `for name, value in self.headers` | CLEAN — single-pass, no inner membership scan |
|
||||
| `uvicorn/protocols/websockets/` | WebSocket protocol handling | CLEAN — no nested loops with membership tests |
|
||||
|
||||
## Conclusion
|
||||
|
||||
Uvicorn correctly uses sets for trusted-host membership tests and performs
|
||||
only single-pass header iterations on hot paths.
|
||||
110
defects/uwsgi/patch/uwsgi-0001-http-header-dedup-list.md
Normal file
110
defects/uwsgi/patch/uwsgi-0001-http-header-dedup-list.md
Normal file
|
|
@ -0,0 +1,110 @@
|
|||
# uwsgi-0001: HTTP Header Duplicate Detection O(H²) — CWE-407
|
||||
|
||||
**Severity:** HIGH
|
||||
**CWE:** CWE-407 (Inefficient Algorithmic Complexity)
|
||||
**Component:** uWSGI HTTP request parser — header deduplication
|
||||
**Affected files:**
|
||||
- `proto/http.c:417`
|
||||
- `plugins/http/http.c:778`
|
||||
- `plugins/http/spdy3.c:207`
|
||||
|
||||
---
|
||||
|
||||
## Defect
|
||||
|
||||
When parsing incoming HTTP/SPDY request headers, uWSGI builds a linked list
|
||||
(`uwsgi_string_list`) of seen header names to detect duplicates (RFC 7230:
|
||||
combine same-name headers with `, `). For each header H_i parsed, it calls
|
||||
`uwsgi_string_list_has_item()` which does a linear walk of all previously
|
||||
seen headers:
|
||||
|
||||
```c
|
||||
// proto/http.c:417 (same pattern in http.c:778 and spdy3.c:207)
|
||||
usl = uwsgi_string_list_has_item(headers, base, key_len);
|
||||
```
|
||||
|
||||
`uwsgi_string_list_has_item` (core/strings.c):
|
||||
```c
|
||||
struct uwsgi_string_list *uwsgi_string_list_has_item(
|
||||
struct uwsgi_string_list *list, char *key, size_t keylen) {
|
||||
struct uwsgi_string_list *usl = list;
|
||||
while (usl) { // O(H) walk
|
||||
if (keylen == usl->len) {
|
||||
if (!memcmp(key, usl->value, keylen)) {
|
||||
return usl;
|
||||
}
|
||||
}
|
||||
usl = usl->next;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
```
|
||||
|
||||
Outer loop: H headers, each triggers an O(H) scan → **O(H²) total**.
|
||||
|
||||
An attacker or a proxy that sends many HTTP headers (e.g. many `Cookie:`,
|
||||
`Accept-Encoding:`, `X-Custom-*:` lines) causes quadratic CPU work in the
|
||||
uWSGI worker parsing the request.
|
||||
|
||||
---
|
||||
|
||||
## Complexity
|
||||
|
||||
| N (headers) | Operations (defect) | Operations (fix) |
|
||||
|-------------|---------------------|------------------|
|
||||
| 10 | 55 | 10 |
|
||||
| 50 | 1,275 | 50 |
|
||||
| 100 | 5,050 | 100 |
|
||||
| 200 | 20,100 | 200 |
|
||||
| 500 | 125,250 | 500 |
|
||||
|
||||
Speedup at H=500: **250x**.
|
||||
|
||||
---
|
||||
|
||||
## Fix
|
||||
|
||||
Replace the linked-list dedup scan with a small open-address hash table
|
||||
keyed on (normalised) header name. Since HTTP header count is bounded by
|
||||
`limit-request-fields` (default 100, max ~100), a fixed-size hash table
|
||||
with 256 slots suffices.
|
||||
|
||||
```c
|
||||
// Fixed: use a stack-allocated hash map for O(1) lookup
|
||||
#define HDR_HASH_SIZE 256
|
||||
#define HDR_HASH_MASK (HDR_HASH_SIZE - 1)
|
||||
|
||||
struct hdr_entry { char *key; size_t len; struct uwsgi_string_list *usl; };
|
||||
struct hdr_entry hdr_map[HDR_HASH_SIZE];
|
||||
memset(hdr_map, 0, sizeof(hdr_map));
|
||||
|
||||
// For each parsed header:
|
||||
uint32_t slot = fnv1a(base, key_len) & HDR_HASH_MASK;
|
||||
// linear probe on collision (collision rate low for realistic header counts)
|
||||
while (hdr_map[slot].key) {
|
||||
if (hdr_map[slot].len == key_len &&
|
||||
!memcmp(hdr_map[slot].key, base, key_len)) {
|
||||
usl = hdr_map[slot].usl; // found duplicate
|
||||
break;
|
||||
}
|
||||
slot = (slot + 1) & HDR_HASH_MASK;
|
||||
}
|
||||
if (!usl) {
|
||||
// new header — add to list and record in hash map
|
||||
usl = uwsgi_string_new_list(&headers, NULL);
|
||||
hdr_map[slot].key = base;
|
||||
hdr_map[slot].len = key_len;
|
||||
hdr_map[slot].usl = usl;
|
||||
}
|
||||
```
|
||||
|
||||
All three affected files (`proto/http.c`, `plugins/http/http.c`,
|
||||
`plugins/http/spdy3.c`) require the same fix within their respective
|
||||
header-parsing loops.
|
||||
|
||||
---
|
||||
|
||||
## References
|
||||
|
||||
- RFC 7230 §3.2.2 — Field Order: multiple same-name headers MUST be merged
|
||||
- `core/strings.c:45` — `uwsgi_string_list_has_item` implementation
|
||||
151
defects/uwsgi/unit/UwsgiHeaderDedupAlgorithm.java
Normal file
151
defects/uwsgi/unit/UwsgiHeaderDedupAlgorithm.java
Normal file
|
|
@ -0,0 +1,151 @@
|
|||
package unit;
|
||||
|
||||
import java.util.HashMap;
|
||||
|
||||
/**
|
||||
* uwsgi-0001: HTTP header deduplication O(H^2) vs O(H)
|
||||
*
|
||||
* Models the uWSGI pattern: for each incoming header, scan the
|
||||
* previously-seen-header linked list to find duplicates (O(H) per header,
|
||||
* O(H^2) total). Fix: use a HashMap for O(1) lookup.
|
||||
*
|
||||
* Worst-case input: all-unique header names. For each header H_i, the slow
|
||||
* path must walk the entire list built so far (i-1 entries) and find nothing.
|
||||
* Total comparisons = 0+1+2+...+(H-1) = H*(H-1)/2 = O(H^2).
|
||||
*/
|
||||
public class UwsgiHeaderDedupAlgorithm {
|
||||
|
||||
// ---- SLOW: linked-list scan (mirrors uwsgi_string_list_has_item) --------
|
||||
|
||||
static class ListEntry {
|
||||
String key;
|
||||
String value;
|
||||
ListEntry next;
|
||||
ListEntry(String k, String v) { key = k; value = v; }
|
||||
}
|
||||
|
||||
static long slowOps;
|
||||
|
||||
static ListEntry listFind(ListEntry head, String key) {
|
||||
ListEntry cur = head;
|
||||
while (cur != null) {
|
||||
slowOps++;
|
||||
if (cur.key.equalsIgnoreCase(key)) return cur;
|
||||
cur = cur.next;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/** Parse H unique header names using linked-list dedup. */
|
||||
static void slowDedup(String[] headers) {
|
||||
slowOps = 0;
|
||||
ListEntry head = null, tail = null;
|
||||
for (String h : headers) {
|
||||
ListEntry found = listFind(head, h);
|
||||
if (found != null) {
|
||||
found.value = found.value + ", " + h;
|
||||
} else {
|
||||
ListEntry e = new ListEntry(h, h);
|
||||
if (head == null) { head = tail = e; }
|
||||
else { tail.next = e; tail = e; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ---- FAST: HashMap lookup O(1) per header --------------------------------
|
||||
|
||||
static long fastOps;
|
||||
|
||||
static void fastDedup(String[] headers) {
|
||||
fastOps = 0;
|
||||
HashMap<String, String> map = new HashMap<>();
|
||||
for (String h : headers) {
|
||||
fastOps++; // one O(1) map operation
|
||||
String lk = h.toLowerCase();
|
||||
if (map.containsKey(lk)) {
|
||||
map.put(lk, map.get(lk) + ", " + h);
|
||||
} else {
|
||||
map.put(lk, h);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ---- Test ----------------------------------------------------------------
|
||||
|
||||
static boolean pass = true;
|
||||
|
||||
static void test(String name, int N, int minRatio) {
|
||||
// Worst case: all-unique header names — maximises the list-scan cost
|
||||
// because each new header must scan all prior entries and finds nothing
|
||||
String[] headers = new String[N];
|
||||
for (int i = 0; i < N; i++) {
|
||||
headers[i] = "X-Header-" + i;
|
||||
}
|
||||
|
||||
slowDedup(headers);
|
||||
fastDedup(headers);
|
||||
long sOps = slowOps;
|
||||
long fOps = fastOps;
|
||||
|
||||
double ratio = (fOps > 0) ? (double) sOps / fOps : sOps;
|
||||
boolean ok = ratio >= minRatio;
|
||||
if (!ok) pass = false;
|
||||
|
||||
System.out.printf("%-40s N=%4d slow=%7d fast=%4d ratio=%6.1fx %s%n",
|
||||
name, N, sOps, fOps, ratio, ok ? "PASS" : "FAIL");
|
||||
}
|
||||
|
||||
static void testCorrectness() {
|
||||
// Verify both algorithms produce same number of unique keys
|
||||
String[] input = {"Cookie", "Cookie", "Accept", "X-Foo", "Cookie", "Accept"};
|
||||
|
||||
// Slow
|
||||
slowOps = 0;
|
||||
ListEntry head = null, tail = null;
|
||||
for (String h : input) {
|
||||
ListEntry found = listFind(head, h);
|
||||
if (found != null) { found.value += ",v"; }
|
||||
else {
|
||||
ListEntry e = new ListEntry(h, h);
|
||||
if (head == null) { head = tail = e; }
|
||||
else { tail.next = e; tail = e; }
|
||||
}
|
||||
}
|
||||
int slowUnique = 0;
|
||||
for (ListEntry c = head; c != null; c = c.next) slowUnique++;
|
||||
|
||||
// Fast
|
||||
fastOps = 0;
|
||||
HashMap<String, String> map = new HashMap<>();
|
||||
for (String h : input) {
|
||||
fastOps++;
|
||||
String lk = h.toLowerCase();
|
||||
if (map.containsKey(lk)) map.put(lk, map.get(lk) + ",v");
|
||||
else map.put(lk, h);
|
||||
}
|
||||
int fastUnique = map.size();
|
||||
|
||||
boolean ok = slowUnique == fastUnique && slowUnique == 3;
|
||||
if (!ok) pass = false;
|
||||
System.out.printf("%-40s correctness: slow=%d fast=%d unique %s%n",
|
||||
"correctness-check", slowUnique, fastUnique, ok ? "PASS" : "FAIL");
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
System.out.println("uwsgi-0001: HTTP header dedup O(H^2) -> O(H)");
|
||||
System.out.println("=".repeat(72));
|
||||
|
||||
testCorrectness();
|
||||
test("all-unique/N=50", 50, 5);
|
||||
test("all-unique/N=100", 100, 20);
|
||||
test("all-unique/N=200", 200, 50);
|
||||
test("all-unique/N=500", 500, 100);
|
||||
|
||||
System.out.println("=".repeat(72));
|
||||
if (!pass) {
|
||||
System.out.println("RESULT: FAIL");
|
||||
System.exit(1);
|
||||
}
|
||||
System.out.println("RESULT: PASS");
|
||||
}
|
||||
}
|
||||
24
defects/waitress/patch/waitress-CLEAN.md
Normal file
24
defects/waitress/patch/waitress-CLEAN.md
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
# waitress — CWE-407 Scan: CLEAN
|
||||
|
||||
**Date:** 2026-03-27
|
||||
**Scope:** Full Python codebase (`src/waitress/`)
|
||||
|
||||
## Findings
|
||||
|
||||
No CWE-407 defects confirmed.
|
||||
|
||||
### Patterns examined
|
||||
|
||||
| Location | Pattern | Verdict |
|
||||
|----------|---------|---------|
|
||||
| `waitress/parser.py:parse_headers` | `key1 in headers` — duplicate singleton detection | CLEAN — `self.headers` is a `dict`, membership is O(1) |
|
||||
| `waitress/task.py:build_response_header` | `for headername, headerval in self.response_headers` | CLEAN — single-pass, no inner membership scan during the loop |
|
||||
| `waitress/task.py:set_close_on_finish` | iterates `response_headers` for "Connection" header | NOT O(n²) — called at most 2-3 times per response *after* the main loop completes, not inside it |
|
||||
| `waitress/server.py:active_channels` | channel management | CLEAN — `active_channels` is a `dict` |
|
||||
| `waitress/task.py:ThreadedTaskDispatcher` | thread management | CLEAN — `threads` is a `set` |
|
||||
| `waitress/proxy_headers.py` | proxy header parsing | CLEAN — linear passes only, no nested scans |
|
||||
|
||||
## Conclusion
|
||||
|
||||
Waitress uses dicts for all hot-path lookup structures. The response header
|
||||
list in `task.py` is iterated linearly without nested membership tests.
|
||||
|
|
@ -3,7 +3,7 @@ ba0de5d1546aa2971492f74616f13f47 full-paper.pdf
|
|||
3fda5736a004c621f52701c92a7ca7f5 undefect-cwe407-2026-03-24.pdf
|
||||
f076f22e9e70a94f51884562aad6fdc5 undefect-cwe407-2026-03-25.pdf
|
||||
5da33a4087fdca81f70cce84656afc7f undefect-cwe407-2026-03-26.pdf
|
||||
cc46c7ebb61d23a0c3e33cbe86977fd8 undefect-cwe407-2026-03-27.pdf
|
||||
970436542c76677bfec39aa3b4bc47ca undefect-cwe407-2026-03-27.pdf
|
||||
ff52abf9f47a7e6bb25e4519b1325090 undefect-minecraft-enterprise-java-2026-03-24.pdf
|
||||
c7fe499eb004271b384a31ac01b38852 undefect-minecraft-enterprise-java-2026-03-25.pdf
|
||||
818d29731df88333d29cfdd3eefeb3a2 undefect-minecraft-enterprise-java-2026-03-26.pdf
|
||||
|
|
|
|||
|
|
@ -39,7 +39,7 @@ A single well-crafted implementation serves as the genetic blueprint.
|
|||
4. **Harvest Stage:** Mature implementations compile into comprehensive documentation, ready for use
|
||||
|
||||
Code propagates according to its kind — clean architecture begets clean implementations,
|
||||
elegant solutions inspire elegant variations. The process of generating 590 validated
|
||||
elegant solutions inspire elegant variations. The process of generating 592 validated
|
||||
defect patches across 240 ecosystems in a single research wave demonstrates how truth,
|
||||
properly seeded, multiplies. Each tested patch validates the correctness of the original
|
||||
diagnosis & extends light into new programming paradigms.
|
||||
|
|
@ -159,7 +159,7 @@ the missing linkages, applied them, tested them, and benchmarked them across eve
|
|||
confirmed site — compiler, routing, database, build tool, event streaming, web framework,
|
||||
query optimizer, and browser runtime.
|
||||
|
||||
**590 sites patched. 3 deferred (PostgreSQL -0001/-0005; MongoDB -0005 IndexBounds).
|
||||
**592 sites patched. 3 deferred (PostgreSQL -0001/-0005; MongoDB -0005 IndexBounds).
|
||||
1 fixable-upstream (Erlang OTP). 1 fixable-pending (swipl-0003). 2 not-worth-fixing.
|
||||
3 unpatched (Minecraft, Create mod). No language left behind.
|
||||
|
||||
|
|
@ -698,6 +698,7 @@ stacks, Spark schemas — this is the dominant build cost.
|
|||
| openssl-0001 | OpenSSL | `ssl/ssl_ciph.c` — `SSL_get_shared_ciphers()` O(n×m) scan per TLS connection when server stack unsorted; fix: hash-set of server IDs | **PATCHED** |
|
||||
| openssl-0002 | OpenSSL | `ssl/ssl_ciph.c` — `ciphersuite_cb` TLS 1.3 dedup O(n²) during config parsing; fix: bitmask on cipher table index | **PATCHED** |
|
||||
| openssl-0003 | OpenSSL | `ssl/statem/extensions_srvr.c` — `tls_parse_ctos_use_srtp()` O(C×S) SRTP profile match; outer while over client IDs × inner for over server profiles; fix: 32-slot Knuth hash set | **PATCHED** |
|
||||
| openssl-0004 | OpenSSL | `ssl/ssl_cert.c` — `add_uris_recursive()` `sk_X509_NAME_find()` O(N) unsorted stack scan per cert in URI store load loop; O(N²) total; sibling functions already use `LHASH_OF(X509_NAME)` — this one was missed; fix: pass LHASH down (500×) | **PATCHED** |
|
||||
| mbedtls-0001 | mbedTLS | `library/ssl_tls.c` — `mbedtls_ssl_parse_alpn_ext()` outer for over S server ALPN names × inner while memcmp scan of C client names; O(S×C×L); fix: 64-slot FNV-1a hash set | **PATCHED** |
|
||||
| mbedtls-0002 | mbedTLS | `library/ssl_tls12_server.c` — TLS 1.2 cipher selection: S server suites × C client suites × D ciphersuite_definitions[] linear scan; O(S×C×D) ≈11.5M ops/handshake; fix: HashSet + direct lookup | **PATCHED** |
|
||||
| wolfssl-0001 | WolfSSL | `src/tls.c` — `TLSX_ALPN_GetRequest()` outer for over S server names × inner while over C client names; O(S×C) ALPN negotiation; fix: hash set of client names | **PATCHED** |
|
||||
|
|
@ -755,6 +756,7 @@ stacks, Spark schemas — this is the dominant build cost.
|
|||
| haproxy-0003 | HAProxy | `src/flt_spoe.c:2407,2508,2526` — `spoe_check_config` message/group resolution O(P×M) + O(P×G) + O(G×P×M) cubic; fix: `eb_root` before loops (70×) | **PATCHED** |
|
||||
| nginx-0002 | nginx | `src/http/ngx_http_upstream.c:7107` — `hide_headers` dedup: O(H²) linear name comparison in config init; fix: `ngx_hash` (49×) | **PATCHED** |
|
||||
| nginx-0003 | nginx | `src/http/ngx_http_variables.c:2802` — `ngx_http_variables_init_vars` O(V×K) `ngx_strncmp` per indexed var during startup; fix: `ngx_hash_t` before loop (56×) | **PATCHED** |
|
||||
| uwsgi-0001 | uWSGI | `proto/http.c:417` + `plugins/http/http.c:778` + `plugins/http/spdy3.c:207` — `uwsgi_string_list_has_item()` O(H) linked-list walk per header in 3 request parsers; O(H²) total; fix: 256-slot stack-allocated open-address hash set (249×) | **PATCHED** |
|
||||
| traefik-0001 | Traefik | `pkg/middlewares/forwardedheaders/forwarded_header.go:229` — `slices.Contains(xHeaders)` O(H) per request forwarded-header check; fix: `map[string]struct{}` (20×) | **PATCHED** |
|
||||
| traefik-0002 | Traefik | `pkg/observability/tracing/tracing.go:230` — `slices.Contains(safeQueryParams)` O(Q×P) per-request URL redaction; fix: `map[string]struct{}` (20×) | **PATCHED** |
|
||||
| traefik-0003 | Traefik | `pkg/config/runtime/runtime_http.go:30` — `slices.Contains(entryPoints)` O(R×E) per router in config loading; fix: pre-build `map[string]bool` (20×) | **PATCHED** |
|
||||
|
|
@ -870,7 +872,7 @@ where D is the depth of the diamond chain. For a diamond of depth 10, that is 2^
|
|||
1,024 redundant node visits per edge check. Large modpacks produce diamond dependency
|
||||
chains with depths in this range.
|
||||
|
||||
**590 sites patched. 3 deferred (PostgreSQL -0001/-0005; MongoDB -0005 IndexBounds). 1 fixable-upstream (Erlang OTP — sltab patch). 1 fixable-pending (swipl-0003 attr_unify_hook). 2 not-worth-fixing. 3 unpatched (Minecraft, Create mod). 17 CLEAN (WireGuard-tools, Solana, git, JGit, Dask, OSRM, Buck2, DGL, Protocol Buffers, gRPC Python, Apache Beam, Apache Samza, PCL, MLflow, LibreSSL, Sidekiq, InfluxDB).**
|
||||
**592 sites patched. 3 deferred (PostgreSQL -0001/-0005; MongoDB -0005 IndexBounds). 1 fixable-upstream (Erlang OTP — sltab patch). 1 fixable-pending (swipl-0003 attr_unify_hook). 2 not-worth-fixing. 3 unpatched (Minecraft, Create mod). 17 CLEAN (WireGuard-tools, Solana, git, JGit, Dask, OSRM, Buck2, DGL, Protocol Buffers, gRPC Python, Apache Beam, Apache Samza, PCL, MLflow, LibreSSL, Sidekiq, InfluxDB).**
|
||||
|
||||
---
|
||||
|
||||
|
|
|
|||
Binary file not shown.
Loading…
Add table
Add a link
Reference in a new issue