diff --git a/defects/camel/patch/camel-0001-route-startup-quadratic-endpoint-scan.md b/defects/camel/patch/camel-0001-route-startup-quadratic-endpoint-scan.md index 94cc33a01..6b9e6f602 100644 --- a/defects/camel/patch/camel-0001-route-startup-quadratic-endpoint-scan.md +++ b/defects/camel/patch/camel-0001-route-startup-quadratic-endpoint-scan.md @@ -1,4 +1,4 @@ -# UNDF: UNDF-2026-000000360 +# UNDF: UNDF-2026-000000158 # camel-0001 — O(R²) Route Startup Endpoint Clash Scan **Severity:** HIGH diff --git a/defects/hibernate/patch/hibernate-0007-buildrecursiveorderedfksecondpasses-diamond.md b/defects/hibernate/patch/hibernate-0007-buildrecursiveorderedfksecondpasses-diamond.md deleted file mode 100644 index 56c603335..000000000 --- a/defects/hibernate/patch/hibernate-0007-buildrecursiveorderedfksecondpasses-diamond.md +++ /dev/null @@ -1,110 +0,0 @@ -# UNDF: (pending) -# hibernate-0007: InFlightMetadataCollectorImpl.buildRecursiveOrderedFkSecondPasses — O(2^D) diamond + O(N²) list scan - -## CWE-407 — Algorithmic Complexity: O(2^D) diamond re-traversal + O(N) List.contains in FK ordering - -| Field | Value | -|--------------|-------| -| ID | hibernate-0007 | -| Severity | HIGH | -| Ecosystem | hibernate | -| Package | hibernate-core | -| File | `hibernate-core/src/main/java/org/hibernate/boot/internal/InFlightMetadataCollectorImpl.java` | -| Lines | 1835–1853 | -| Complexity | O(2^D) on diamond FK dependency graphs; O(N) dedup guard | -| Hot path | Called during schema bootstrap: `processSecondPasses()` → FK ordering phase | - -## Defect - -`buildRecursiveOrderedFkSecondPasses` recursively traverses the FK dependency graph -to produce a topologically-ordered list of `FkSecondPass` operations. It uses `startTable` -as a cycle guard (skips re-entering the starting table), but has no guard for diamond -re-traversal of intermediate shared tables: - -```java -// InFlightMetadataCollectorImpl.java:1835-1853 (DEFECT) -private void buildRecursiveOrderedFkSecondPasses( - List orderedFkSecondPasses, - Map> isADependencyOf, - String startTable, - String currentTable) { - final Set dependencies = isADependencyOf.get( currentTable ); - if ( dependencies != null ) { - for ( var fkSecondPass : dependencies ) { - final String dependentTable = fkSecondPass.getValue().getTable()...render(); - if ( dependentTable.compareTo( startTable ) != 0 ) { - buildRecursiveOrderedFkSecondPasses( // recurse — only guards startTable cycle, - orderedFkSecondPasses, isADependencyOf, startTable, dependentTable ); // NOT diamond - } - if ( !orderedFkSecondPasses.contains( fkSecondPass ) ) { // O(N) List.contains! - orderedFkSecondPasses.add( 0, fkSecondPass ); - } - } - } -} -``` - -Two distinct defects: - -1. **Diamond re-traversal O(2^D):** On a diamond FK dependency graph - (T1 depends on T2 and T3; both T2 and T3 depend on T4), T4 is visited twice, 2^D times - at depth D. The `startTable` guard only prevents cycles back to T1, not intermediate diamonds. - -2. **O(N) List.contains dedup guard:** `orderedFkSecondPasses.contains(fkSecondPass)` is an - O(N) scan of the already-ordered list. With N FK passes and diamond re-traversal, - total cost: O(2^D × N). Even without diamonds, N passes each potentially visiting N - already-ordered entries: O(N²). - -## Fix - -Add a `Set visited` parameter to track globally-visited tables; replace -`List.contains` with a `LinkedHashSet` for O(1) dedup: - -```java -// Call site — line 1804-1806 -final LinkedHashSet orderedFkSecondPasses = new LinkedHashSet<>( fkSecondPassList.size() ); -for ( String tableName : isADependencyOf.keySet() ) { - buildRecursiveOrderedFkSecondPasses( orderedFkSecondPasses, isADependencyOf, tableName, tableName, new HashSet<>() ); -} -// process the ordered passes (LinkedHashSet preserves insertion order) -for ( var sp : orderedFkSecondPasses ) { - sp.doSecondPass( getEntityBindingMap() ); -} - -// AFTER — O(N+E) total -private void buildRecursiveOrderedFkSecondPasses( - LinkedHashSet orderedFkSecondPasses, // O(1) add/contains - Map> isADependencyOf, - String startTable, - String currentTable, - Set visited) { // diamond guard - if ( !visited.add( currentTable ) ) { - return; // already traversed this table in this pass - } - final Set dependencies = isADependencyOf.get( currentTable ); - if ( dependencies != null ) { - for ( var fkSecondPass : dependencies ) { - final String dependentTable = fkSecondPass.getValue().getTable().getQualifiedTableName().render(); - if ( dependentTable.compareTo( startTable ) != 0 ) { - buildRecursiveOrderedFkSecondPasses( orderedFkSecondPasses, isADependencyOf, - startTable, dependentTable, visited ); - } - orderedFkSecondPasses.add( fkSecondPass ); // O(1) dedup via LinkedHashSet - } - } -} -``` - -Note: `LinkedHashSet` preserves insertion order (same semantics as `add(0, ...)` reversed) -and provides O(1) `add`/`contains`. The `add(0, ...)` pattern builds the list in reverse -topological order; `LinkedHashSet` with final reversal achieves the same. - -## Speedup - -| Diamond depth (D), N=100 passes | Before (visits) | After (visits) | Speedup | -|---------------------------------|----------------|----------------|---------| -| 5 | 3,100 | 100 | 31× | -| 10 | 102,300 | 100 | 1,023× | -| 15 | 3,276,700 | 100 | 32,767× | - -Growth before: O(2^D × N). Growth after: O(N). diff --git a/defects/quarkus/patch/quarkus-0003-beandeployment-recursive-build-diamond.md b/defects/quarkus/patch/quarkus-0003-beandeployment-recursive-build-diamond.md deleted file mode 100644 index 4ffa477d6..000000000 --- a/defects/quarkus/patch/quarkus-0003-beandeployment-recursive-build-diamond.md +++ /dev/null @@ -1,94 +0,0 @@ -# UNDF: UNDF-2026-000000469 -# quarkus-0003: BeanDeployment.recursiveBuild — O(2^D) diamond re-traversal, no visited guard - -## CWE-407 — Algorithmic Complexity: O(2^D) recursive diamond re-traversal in transitive interceptor binding resolution - -| Field | Value | -|--------------|-------| -| ID | quarkus-0003 | -| Severity | HIGH | -| Ecosystem | quarkus | -| Package | quarkus-arc-processor | -| File | `independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/BeanDeployment.java` | -| Lines | 955–965 | -| Complexity | O(2^D) on diamond interceptor binding hierarchies | -| Hot path | Called at CDI container startup: `findTransitiveInterceptorBindings()` | - -## Defect - -`BeanDeployment.recursiveBuild(DotName name, Map> transitiveBindingsMap)` -computes the transitive closure of interceptor bindings without a visited guard: - -```java -// independent-projects/arc/processor/.../BeanDeployment.java:955-965 (DEFECT) -private static Set recursiveBuild(DotName name, - Map> transitiveBindingsMap) { - Set result = transitiveBindingsMap.get(name); // reference, not copy! - for (AnnotationInstance instance : transitiveBindingsMap.get(name)) { // iterating same set - if (transitiveBindingsMap.containsKey(instance.name())) { - // recursively find — no visited guard - result.addAll(recursiveBuild(instance.name(), transitiveBindingsMap)); // DEFECT 1: mutates result during iteration - } - } - return result; -} -``` - -Two distinct defects: - -1. **Diamond re-traversal O(2^D):** No visited guard. On a diamond binding hierarchy - (A→B, A→C, B→D, C→D), D is visited twice, 2^D times at depth D. - -2. **Live-set mutation:** `result` is a reference to `transitiveBindingsMap.get(name)` — the - same set being iterated. `result.addAll(recursiveBuild(...))` mutates it mid-iteration. - Any newly-added element could cause `ConcurrentModificationException` on the next - `instance` step, depending on Java's iterator implementation. This is undefined behavior. - -## Fix - -Add a `visited` set parameter; use a defensive copy of the initial bindings: - -```java -// AFTER — O(N+E) total, no CME risk -private static Set recursiveBuild(DotName name, - Map> transitiveBindingsMap) { - return recursiveBuild(name, transitiveBindingsMap, new HashSet<>()); -} - -private static Set recursiveBuild(DotName name, - Map> transitiveBindingsMap, - Set visited) { - if (!visited.add(name)) { - return Collections.emptySet(); // diamond guard: already computed for this name - } - Set initial = transitiveBindingsMap.get(name); - Set result = new HashSet<>(initial); // defensive copy — safe to mutate - for (AnnotationInstance instance : initial) { - if (transitiveBindingsMap.containsKey(instance.name())) { - result.addAll(recursiveBuild(instance.name(), transitiveBindingsMap, visited)); - } - } - return result; -} -``` - -Also update the call site to store back the result: -```java -// findTransitiveInterceptorBindings — line 949-951 -for (DotName name : result.keySet()) { - result.put(name, recursiveBuild(name, result)); -} -``` -The call site is already correct — it stores the returned set back. The fix makes the returned -set a fresh copy rather than a mutation of the shared map entry. - -## 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). diff --git a/defects/rabbitmq/patch/rmq-0001-classic-queue-pending-map.patch b/defects/rabbitmq/patch/rmq-0001-classic-queue-pending-map.patch index 7c999c357..9a92e25ae 100644 --- a/defects/rabbitmq/patch/rmq-0001-classic-queue-pending-map.patch +++ b/defects/rabbitmq/patch/rmq-0001-classic-queue-pending-map.patch @@ -1,4 +1,4 @@ -# UNDF: UNDF-2026-000000237 +# UNDF: UNDF-2026-000000239 --- a/deps/rabbit/src/rabbit_classic_queue.erl +++ b/deps/rabbit/src/rabbit_classic_queue.erl @@ -9,7 +9,7 @@ diff --git a/defects/rabbitmq/patch/rmq-0002-sac-coordinator-gb-sets.patch b/defects/rabbitmq/patch/rmq-0002-sac-coordinator-gb-sets.patch index 4c602a911..9e307deed 100644 --- a/defects/rabbitmq/patch/rmq-0002-sac-coordinator-gb-sets.patch +++ b/defects/rabbitmq/patch/rmq-0002-sac-coordinator-gb-sets.patch @@ -1,4 +1,4 @@ -# UNDF: UNDF-2026-000000238 +# UNDF: UNDF-2026-000000240 --- a/deps/rabbit/src/rabbit_stream_sac_coordinator.erl +++ b/deps/rabbit/src/rabbit_stream_sac_coordinator.erl @@ -200,15 +200,18 @@ filter_dead_pids(Pids) -> diff --git a/defects/rabbitmq/patch/rmq-0003-check-declare-args-sets.patch b/defects/rabbitmq/patch/rmq-0003-check-declare-args-sets.patch index 36feaf5d9..bd9727b51 100644 --- a/defects/rabbitmq/patch/rmq-0003-check-declare-args-sets.patch +++ b/defects/rabbitmq/patch/rmq-0003-check-declare-args-sets.patch @@ -1,4 +1,4 @@ -# UNDF: UNDF-2026-000000239 +# UNDF: UNDF-2026-000000360 --- a/deps/rabbit/src/rabbit_amqqueue.erl +++ b/deps/rabbit/src/rabbit_amqqueue.erl @@ -902,12 +902,14 @@ check_declare_arguments(QueueName, Args0, DefaultQueueType) -> diff --git a/defects/rabbitmq/patch/rmq-0004-check-arguments-key-sets.patch b/defects/rabbitmq/patch/rmq-0004-check-arguments-key-sets.patch index 2f0639d6e..2fd0880d8 100644 --- a/defects/rabbitmq/patch/rmq-0004-check-arguments-key-sets.patch +++ b/defects/rabbitmq/patch/rmq-0004-check-arguments-key-sets.patch @@ -1,4 +1,4 @@ -# UNDF: UNDF-2026-000000240 +# UNDF: UNDF-2026-000000387 --- a/deps/rabbit/src/rabbit_amqqueue.erl +++ b/deps/rabbit/src/rabbit_amqqueue.erl @@ -932,11 +932,12 @@ check_arguments_key(QueueName, QueueType, Args, InvalidArgs) -> diff --git a/defects/rabbitmq/patch/rmq-0005-export-binding-qnames-sets.md b/defects/rabbitmq/patch/rmq-0005-export-binding-qnames-sets.md index 915c039ca..6684fc95d 100644 --- a/defects/rabbitmq/patch/rmq-0005-export-binding-qnames-sets.md +++ b/defects/rabbitmq/patch/rmq-0005-export-binding-qnames-sets.md @@ -1,4 +1,4 @@ -# UNDF: UNDF-2026-000000520 +# UNDF: UNDF-2026-000000473 # rmq-0005: rabbit_mgmt_wm_definitions export_binding O(B×Q) → O(B+Q) ## Location