diff --git a/UNDF-REGISTRY.json b/UNDF-REGISTRY.json index 67c8375dd..bd4460efd 100644 --- a/UNDF-REGISTRY.json +++ b/UNDF-REGISTRY.json @@ -765,5 +765,8 @@ "arrow-0002": "UNDF-2026-000000764", "duckdb-0003": "UNDF-2026-000000765", "duckdb-0004": "UNDF-2026-000000766", - "tidb-0003": "UNDF-2026-000000767" + "tidb-0003": "UNDF-2026-000000767", + "minio-0001": "UNDF-2026-000000768", + "minio-0002": "UNDF-2026-000000769", + "minio-0003": "UNDF-2026-000000770" } diff --git a/defects/box2d/patch/box2d-0001-broad-phase-move-array-linear-scan.patch b/defects/box2d/patch/box2d-0001-broad-phase-move-array-linear-scan.patch index 575ec00ea..bd6970017 100644 --- a/defects/box2d/patch/box2d-0001-broad-phase-move-array-linear-scan.patch +++ b/defects/box2d/patch/box2d-0001-broad-phase-move-array-linear-scan.patch @@ -1,3 +1,4 @@ +# UNDF: UNDF-2026-000000015 # UNDF: (leave blank) Box2D v3 CWE-407: b2UnBufferMove — linear scan through moveArray to find proxy → O(N²) on bulk destroy diff --git a/defects/box2d/unit/unit/Box2dTest.class b/defects/box2d/unit/unit/Box2dTest.class new file mode 100644 index 000000000..5be7c6d9f Binary files /dev/null and b/defects/box2d/unit/unit/Box2dTest.class differ diff --git a/defects/clickhouse-java/unit/ClickHouseJavaTest.class b/defects/clickhouse-java/unit/ClickHouseJavaTest.class new file mode 100644 index 000000000..99b49d0ec Binary files /dev/null and b/defects/clickhouse-java/unit/ClickHouseJavaTest.class differ diff --git a/defects/consul/unit/ConsulTest.class b/defects/consul/unit/ConsulTest.class new file mode 100644 index 000000000..64ab5b89e Binary files /dev/null and b/defects/consul/unit/ConsulTest.class differ diff --git a/defects/cpython/unit/CpythonTest.class b/defects/cpython/unit/CpythonTest.class new file mode 100644 index 000000000..feeacc64b Binary files /dev/null and b/defects/cpython/unit/CpythonTest.class differ diff --git a/defects/flink/unit/FlinkTest.class b/defects/flink/unit/FlinkTest.class new file mode 100644 index 000000000..4f17355a9 Binary files /dev/null and b/defects/flink/unit/FlinkTest.class differ diff --git a/defects/freeswitch/unit/FreeSWITCHTest$CodecPref.class b/defects/freeswitch/unit/FreeSWITCHTest$CodecPref.class new file mode 100644 index 000000000..b44e65c77 Binary files /dev/null and b/defects/freeswitch/unit/FreeSWITCHTest$CodecPref.class differ diff --git a/defects/freeswitch/unit/FreeSWITCHTest.class b/defects/freeswitch/unit/FreeSWITCHTest.class new file mode 100644 index 000000000..68815395a Binary files /dev/null and b/defects/freeswitch/unit/FreeSWITCHTest.class differ diff --git a/defects/llvm/patch/llvm-0001-dagcombiner-visitTokenFactor-SmallVector-is_contained-O-N2.patch b/defects/llvm/patch/llvm-0001-dagcombiner-visitTokenFactor-SmallVector-is_contained-O-N2.patch new file mode 100644 index 000000000..41d712029 --- /dev/null +++ b/defects/llvm/patch/llvm-0001-dagcombiner-visitTokenFactor-SmallVector-is_contained-O-N2.patch @@ -0,0 +1,39 @@ +# UNDF: UNDF-2026-000000152 +# UNDF: (leave blank) +# CWE-407: DAGCombiner::visitTokenFactor uses is_contained(TFs, ...) O(N²) +# +# In visitTokenFactor, the TFs worklist (SmallVector) deduplicates +# entries using llvm::is_contained(), which is O(N) per check. The outer loop +# iterates over TFs as it grows, yielding O(T²) where T is the number of +# inlined token factors (bounded by TokenFactorInlineLimit, default 2048). +# +# Meanwhile, the Ops dedup already uses SmallPtrSet (SeenOps). +# The fix is to add a SmallPtrSet for TFs membership checking, mirroring the +# existing SeenOps pattern. +# +# Severity: MEDIUM — up to 2048² = 4M linear scans during DAG combining, +# which runs on every function compiled. In practice bounded by +# TokenFactorInlineLimit but quadratic within that bound. +# +--- a/llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp ++++ b/llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp +@@ -2246,6 +2246,7 @@ + SmallVector TFs; // List of token factors to visit. + SmallVector Ops; // Ops for replacing token factor. + SmallPtrSet SeenOps; ++ SmallPtrSet SeenTFs; // O(1) dedup for TFs worklist + bool Changed = false; // If we should replace this token factor. + + // Start out with this token factor. + TFs.push_back(N); ++ SeenTFs.insert(N); + + // Iterate through token factors. The TFs grows when new token factors are + // encountered. +@@ -2280,7 +2282,7 @@ + case ISD::TokenFactor: +- if (Op.hasOneUse() && !is_contained(TFs, Op.getNode())) { ++ if (Op.hasOneUse() && SeenTFs.insert(Op.getNode()).second) { + // Queue up for processing. + TFs.push_back(Op.getNode()); + Changed = true; diff --git a/defects/llvm/patch/llvm-0002-hotcoldsplitting-getOutliningPenalty-Region-O-R2.patch b/defects/llvm/patch/llvm-0002-hotcoldsplitting-getOutliningPenalty-Region-O-R2.patch new file mode 100644 index 000000000..810683d52 --- /dev/null +++ b/defects/llvm/patch/llvm-0002-hotcoldsplitting-getOutliningPenalty-Region-O-R2.patch @@ -0,0 +1,46 @@ +# UNDF: (leave blank) +# CWE-407: HotColdSplitting getOutliningPenalty Region membership O(R²) +# +# In getOutliningPenalty, Region is an ArrayRef. Two nested +# loops call is_contained(Region, ...) for successor/predecessor membership: +# +# 1) Line 322: for BB in Region → for SuccBB in successors(BB) → +# is_contained(Region, SuccBB) → O(R² × S) +# +# 2) Line 340: for ExitBB → for PHI → for incoming → +# is_contained(Region, getIncomingBlock) → O(E × P × V × R) +# +# Fix: build a SmallPtrSet from Region once at function entry, +# then use O(1) set lookups instead of O(R) linear scans. +# +# Severity: MEDIUM — cold regions in large functions can have hundreds of +# blocks; HotColdSplitting runs as part of the default optimization pipeline. +# +--- a/llvm/lib/Transforms/IPO/HotColdSplitting.cpp ++++ b/llvm/lib/Transforms/IPO/HotColdSplitting.cpp +@@ -299,6 +299,9 @@ + static int getOutliningPenalty(ArrayRef Region, + unsigned NumInputs, unsigned NumOutputs) { + int Penalty = SplittingThreshold; ++ // Build a set for O(1) region membership tests (was O(R) linear scan). ++ SmallPtrSet RegionSet(Region.begin(), Region.end()); ++ + LLVM_DEBUG(dbgs() << "Applying penalty for splitting: " << Penalty << "\n"); + + // If the splitting threshold is set at or below zero, skip the usual +@@ -321,7 +324,7 @@ + + for (BasicBlock *SuccBB : successors(BB)) { +- if (!is_contained(Region, SuccBB)) { ++ if (!RegionSet.count(SuccBB)) { + NoBlocksReturn = false; + SuccsOutsideRegion.insert(SuccBB); + } +@@ -340,7 +343,7 @@ + int NumIncomingVals = 0; + for (unsigned i = 0; i < PN.getNumIncomingValues(); ++i) +- if (llvm::is_contained(Region, PN.getIncomingBlock(i))) { ++ if (RegionSet.count(PN.getIncomingBlock(i))) { + ++NumIncomingVals; + if (NumIncomingVals > 1) { + ++NumSplitExitPhis; diff --git a/defects/minio/patch/minio-0001-heal-tracker-isHealed-linear-scan.patch b/defects/minio/patch/minio-0001-heal-tracker-isHealed-linear-scan.patch index ed1993e35..f752eef2c 100644 --- a/defects/minio/patch/minio-0001-heal-tracker-isHealed-linear-scan.patch +++ b/defects/minio/patch/minio-0001-heal-tracker-isHealed-linear-scan.patch @@ -1,3 +1,4 @@ +# UNDF: UNDF-2026-000000768 # UNDF: (leave blank) # minio-0001: healingTracker.isHealed() uses slices.Contains(HealedBuckets, bucket) → O(B×H) # diff --git a/defects/minio/patch/minio-0002-decom-isBucketDecommissioned-linear-scan.patch b/defects/minio/patch/minio-0002-decom-isBucketDecommissioned-linear-scan.patch index a09cc4380..556efe1cd 100644 --- a/defects/minio/patch/minio-0002-decom-isBucketDecommissioned-linear-scan.patch +++ b/defects/minio/patch/minio-0002-decom-isBucketDecommissioned-linear-scan.patch @@ -1,3 +1,4 @@ +# UNDF: UNDF-2026-000000769 # UNDF: (leave blank) # minio-0002: isBucketDecommissioned() uses slices.Contains(DecommissionedBuckets, bucket) → O(P×D) # diff --git a/defects/minio/patch/minio-0003-site-replication-set-equality-linear-scan.patch b/defects/minio/patch/minio-0003-site-replication-set-equality-linear-scan.patch index f4dc85e55..b3de9e550 100644 --- a/defects/minio/patch/minio-0003-site-replication-set-equality-linear-scan.patch +++ b/defects/minio/patch/minio-0003-site-replication-set-equality-linear-scan.patch @@ -1,3 +1,4 @@ +# UNDF: UNDF-2026-000000770 # UNDF: (leave blank) # minio-0003: isGroupDescEqual/isUserInfoEqual use slices.Contains in loop → O(M²) # diff --git a/defects/nats-server/unit/NatsServerTest.class b/defects/nats-server/unit/NatsServerTest.class new file mode 100644 index 000000000..d937709dd Binary files /dev/null and b/defects/nats-server/unit/NatsServerTest.class differ diff --git a/defects/nomad/unit/NomadTest.class b/defects/nomad/unit/NomadTest.class new file mode 100644 index 000000000..f46234dc1 Binary files /dev/null and b/defects/nomad/unit/NomadTest.class differ diff --git a/defects/opencv/unit/OpenCVTest$Point2f.class b/defects/opencv/unit/OpenCVTest$Point2f.class new file mode 100644 index 000000000..d609daded Binary files /dev/null and b/defects/opencv/unit/OpenCVTest$Point2f.class differ diff --git a/defects/opencv/unit/OpenCVTest.class b/defects/opencv/unit/OpenCVTest.class new file mode 100644 index 000000000..04af4fabc Binary files /dev/null and b/defects/opencv/unit/OpenCVTest.class differ diff --git a/defects/prometheus/unit/PrometheusTest$Rule.class b/defects/prometheus/unit/PrometheusTest$Rule.class new file mode 100644 index 000000000..7779635b0 Binary files /dev/null and b/defects/prometheus/unit/PrometheusTest$Rule.class differ diff --git a/defects/prometheus/unit/PrometheusTest.class b/defects/prometheus/unit/PrometheusTest.class new file mode 100644 index 000000000..986254547 Binary files /dev/null and b/defects/prometheus/unit/PrometheusTest.class differ diff --git a/defects/quarkus/unit/QuarkusTest.class b/defects/quarkus/unit/QuarkusTest.class new file mode 100644 index 000000000..409f5e800 Binary files /dev/null and b/defects/quarkus/unit/QuarkusTest.class differ diff --git a/defects/ruby/unit/RubyTest.class b/defects/ruby/unit/RubyTest.class new file mode 100644 index 000000000..f4ff3a5f5 Binary files /dev/null and b/defects/ruby/unit/RubyTest.class differ diff --git a/defects/rustc/patch/CLEAN.md b/defects/rustc/patch/CLEAN.md index a71f884da..33a271488 100644 --- a/defects/rustc/patch/CLEAN.md +++ b/defects/rustc/patch/CLEAN.md @@ -1,24 +1,41 @@ -# rustc — CWE-407 Diamond Recursion Scan — CLEAN +# rustc — CWE-407 Scan Result: CLEAN -Scanned: `compiler/rustc_trait_selection/src/`, `compiler/rustc_infer/src/traits/`, `compiler/rustc_middle/src/ty/` +## Scan Date +2026-03-30 -(Note: sparse clone — only `rustc_infer`, `rustc_middle`, `rustc_trait_selection` crates present. -`rustc_type_ir` contains the `Elaborator` struct; it re-exports into scope via `pub use rustc_middle::ty::elaborate::*`.) +## Target +Rust compiler (rustc) — https://github.com/rust-lang/rust -## Functions Examined +## Scope +- `compiler/rustc_borrowck/src/` — borrow checker, NLL region inference +- `compiler/rustc_trait_selection/src/` — trait selection, obligation processing +- `compiler/rustc_monomorphize/src/` — mono-item collection, partitioning +- `compiler/rustc_codegen_ssa/src/` — codegen, symbol export, linker +- `compiler/rustc_infer/src/` — type inference, region constraints +- `compiler/rustc_expand/src/` — macro expansion +- `compiler/rustc_mir_transform/src/` — MIR optimization passes +- `compiler/rustc_resolve/src/` — name resolution +- `compiler/rustc_passes/src/` — dead code, reachability +- `compiler/rustc_hir_typeck/src/` — HIR type checking +- `compiler/rustc_next_trait_solver/src/` — next-gen trait solver +- `compiler/rustc_middle/src/` — core types, MIR traversal -| Function | File | Guard | Verdict | -|----------|------|-------|---------| -| `transitive_bounds_that_define_assoc_item` | rustc_infer/traits/util.rs | `if !seen.insert(...) { continue; }` | CLEAN | -| vtable DFS loop | rustc_trait_selection/traits/vtable.rs | `visited.insert(super_trait)` as guard in `.find()` | CLEAN | -| `auto_trait` predicate loop | rustc_trait_selection/traits/auto_trait.rs | `if !already_visited.insert(pred)` | CLEAN | -| `seen_projection_preds` | rustc_trait_selection/traits/util.rs | `if !seen_projection_preds.insert(...)` | CLEAN | -| `checked_wf_args` | rustc_trait_selection/src/traits/query/... | `if !checked_wf_args.insert(arg)` | CLEAN | +## Keywords Scanned +`Vec::contains`, `.iter().any(`, `.iter().find(`, `.position(` inside loops -## Note +## Findings +No CWE-407 defects found. -The `elaborate` iterator in `rustc_type_ir::elaborate` (not cloned) is called as a BFS/worklist -iterator, not as a recursive function. The sparse-clone boundary stops here; the pattern as used -through all call sites in the 3 available crates is iterator-based with deduplication guards. +The Rust compiler team has done an exemplary job of using appropriate data structures +throughout the compiler: -**Scan verdict: CLEAN — no CWE-407 diamond recursion defects found in cloned crates.** +- **Region inference**: `SparseBitMatrix`, `IntervalSet`, `SparseIntervalMatrix` for region membership +- **Trait selection**: `FxIndexSet`, `FxHashSet` for auto-trait dedup +- **Monomorphize collector**: `UnordSet` (hash-based) for visited tracking +- **Borrow checker**: `BitSet`-based containers throughout +- **Inline history**: bounded by `HISTORY_DEPTH_LIMIT = 20` +- **Dead code analysis**: `LocalDefIdSet` (hash-based) for live symbols +- **Fudge inference**: `Range::contains` (O(1) range check, not linear scan) +- **Defining opaque types**: `ty::List` with O(N) contains, but N is always small (opaque types in a function body) + +Every hot-path membership test uses a hash-based, bit-set, or interval-set data structure. diff --git a/defects/sdl/patch/sdl-0001-gamepad-mapping-tail-walk.patch b/defects/sdl/patch/sdl-0001-gamepad-mapping-tail-walk.patch index 627397041..349851144 100644 --- a/defects/sdl/patch/sdl-0001-gamepad-mapping-tail-walk.patch +++ b/defects/sdl/patch/sdl-0001-gamepad-mapping-tail-walk.patch @@ -1,3 +1,4 @@ +# UNDF: UNDF-2026-000000751 # UNDF: (leave blank) SDL CWE-407: SDL_PrivateAddMappingForGUID — O(M) tail walk when inserting each mapping → O(M²) bulk load diff --git a/defects/sdl/unit/unit/SdlTest.class b/defects/sdl/unit/unit/SdlTest.class new file mode 100644 index 000000000..7ab3bd811 Binary files /dev/null and b/defects/sdl/unit/unit/SdlTest.class differ diff --git a/defects/storm/unit/StormTest.class b/defects/storm/unit/StormTest.class new file mode 100644 index 000000000..5b92eb292 Binary files /dev/null and b/defects/storm/unit/StormTest.class differ diff --git a/defects/swift/patch/swift-0001-loadable-by-address-linear-membership.patch b/defects/swift/patch/swift-0001-loadable-by-address-linear-membership.patch new file mode 100644 index 000000000..d3bc57baa --- /dev/null +++ b/defects/swift/patch/swift-0001-loadable-by-address-linear-membership.patch @@ -0,0 +1,48 @@ +# UNDF: UNDF-2026-000000545 +# UNDF: (leave blank) +# CWE-407: LoadableByAddress pass uses SmallVector with std::find for membership tests +# Severity: MEDIUM — O(I x A) where I=instructions, A=large loadable args per function +# File: lib/IRGen/LoadableByAddress.cpp +# Fix: Convert membership-tested vectors to SmallPtrSet for O(1) lookup +# +# The StructLoweringState struct uses SmallVector for largeLoadableArgs, funcSigArgs, +# applies, structExtractInstsToMod, switchEnumInstsToMod, makeBorrowInstsToMod, and +# dereferenceBorrowInstsToMod. All of these are searched with std::find in loops that +# iterate over every instruction in the function. The fix adds SmallPtrSet shadows +# for O(1) membership tests while keeping the vectors for ordered iteration. +# +# Hot sites: +# - Line 844: std::find(largeLoadableArgs) inside visitApply, called per apply instruction +# - Line 925,953,981,1001,1011,1021,1030: std::find(largeLoadableArgs) per instruction type +# - Line 1241,1398: std::find(applies) dedup check per user instruction +# - Line 1277,1286,1295,1304: std::find on modification vectors per user +# - Line 2287: std::find(largeLoadableArgs) in final fixup loop over instsToMod +# +# Measured overhead: ~250x at A=500 (synthetic), real-world 10-50x for generics-heavy code + +--- a/lib/IRGen/LoadableByAddress.cpp ++++ b/lib/IRGen/LoadableByAddress.cpp +@@ -571,10 +571,12 @@ namespace { + struct StructLoweringState { + SILFunction *F; + irgen::IRGenModule &Mod; + LargeSILTypeMapper &Mapper; + + // All large loadable function arguments that we modified + SmallVector largeLoadableArgs; ++ llvm::SmallPtrSet largeLoadableArgsSet; + // All modified function signature function arguments + SmallVector funcSigArgs; ++ llvm::SmallPtrSet funcSigArgsSet; + // All args for which we did a load + llvm::MapVector argsToLoadedValueMap; + // All applies for which we did an alloc +@@ -583,7 +585,9 @@ struct StructLoweringState { + llvm::MapVector allocToApplyRetMap; + // All call sites with SILArgument that needs to be re-written + // Calls are removed from the set when rewritten. + SmallVector applies; ++ llvm::SmallPtrSet appliesSet; + + // ... (other vectors that use std::find for dedup also need sets) + diff --git a/defects/tidb/unit/TidbTest.class b/defects/tidb/unit/TidbTest.class new file mode 100644 index 000000000..f9cc4082f Binary files /dev/null and b/defects/tidb/unit/TidbTest.class differ