undf: assign 768-770; stamp minio patches

This commit is contained in:
russell@unturf.com 2026-03-30 10:25:20 -04:00
parent cb853893e5
commit 79a77db4f8
28 changed files with 176 additions and 18 deletions

View file

@ -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"
}

View file

@ -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

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View file

@ -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<SDNode*, 8>) 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<SDNode*, 16> (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<SDNode *, 8> TFs; // List of token factors to visit.
SmallVector<SDValue, 8> Ops; // Ops for replacing token factor.
SmallPtrSet<SDNode*, 16> SeenOps;
+ SmallPtrSet<SDNode*, 16> 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;

View file

@ -0,0 +1,46 @@
# UNDF: (leave blank)
# CWE-407: HotColdSplitting getOutliningPenalty Region membership O(R²)
#
# In getOutliningPenalty, Region is an ArrayRef<BasicBlock*>. 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<BasicBlock*> 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<BasicBlock *> Region,
unsigned NumInputs, unsigned NumOutputs) {
int Penalty = SplittingThreshold;
+ // Build a set for O(1) region membership tests (was O(R) linear scan).
+ SmallPtrSet<BasicBlock *, 16> 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;

View file

@ -1,3 +1,4 @@
# UNDF: UNDF-2026-000000768
# UNDF: (leave blank)
# minio-0001: healingTracker.isHealed() uses slices.Contains(HealedBuckets, bucket) → O(B×H)
#

View file

@ -1,3 +1,4 @@
# UNDF: UNDF-2026-000000769
# UNDF: (leave blank)
# minio-0002: isBucketDecommissioned() uses slices.Contains(DecommissionedBuckets, bucket) → O(P×D)
#

View file

@ -1,3 +1,4 @@
# UNDF: UNDF-2026-000000770
# UNDF: (leave blank)
# minio-0003: isGroupDescEqual/isUserInfoEqual use slices.Contains in loop → O(M²)
#

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View file

@ -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<T>::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.

View file

@ -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

Binary file not shown.

Binary file not shown.

View file

@ -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<SILValue, 16> largeLoadableArgs;
+ llvm::SmallPtrSet<SILValue, 16> largeLoadableArgsSet;
// All modified function signature function arguments
SmallVector<SILValue, 16> funcSigArgs;
+ llvm::SmallPtrSet<SILValue, 16> funcSigArgsSet;
// All args for which we did a load
llvm::MapVector<SILValue, SILValue> argsToLoadedValueMap;
// All applies for which we did an alloc
@@ -583,7 +585,9 @@ struct StructLoweringState {
llvm::MapVector<SILInstruction *, SILInstruction *> allocToApplyRetMap;
// All call sites with SILArgument that needs to be re-written
// Calls are removed from the set when rewritten.
SmallVector<SILInstruction *, 16> applies;
+ llvm::SmallPtrSet<SILInstruction *, 16> appliesSet;
// ... (other vectors that use std::find for dedup also need sets)

Binary file not shown.