undefect. CWE-407 — 63 sites patched across 27 ecosystems

Authors: russell@unturf.com · brackishbert@gmail.com · foxhop.net · TimeHexOn.com

Patches, unit tests, benchmarks, whitepaper, and outreach briefs.
Public domain — no copyright claimed. Use freely.
This commit is contained in:
russell@unturf.com 2026-03-26 17:11:57 -04:00
commit 0a580b313d
70422 changed files with 17213626 additions and 0 deletions

View file

@ -0,0 +1,220 @@
package support;
import java.util.*;
/**
* MongodbPipelineAlgorithm — models two CWE-407 sites in MongoDB aggregation pipeline.
*
* ── mongodb-0001 document_source_streaming_group.cpp:142 (MEDIUM) ──────────
*
* DEFECT: During $group stage initialization, a list of id field names is built
* from the BSON spec (idFieldNames vector). For each element in monotonicIdFields,
* the code calls std::find(idFieldNames.begin(), idFieldNames.end(), fieldName)
* to resolve the index position. With N id fields and M monotonic fields, this
* is O(N × M) — quadratic when N ≈ M.
*
* Site: document_source_streaming_group.cpp:142
* for (const auto& fieldNameElem : monotonicIdFields) {
* auto it = std::find(idFieldNames.begin(), idFieldNames.end(), fieldName);
* // O(N) per iteration
* }
*
* Fix: Build an unordered_map<StringData, size_t> from idFieldNames once, then
* resolve each monotonicIdField in O(1). Total: O(N + M) instead of O(N × M).
*
* ── mongodb-0002 document_source_internal_unpack_bucket.cpp:1076 (HIGH) ────
*
* DEFECT: During pipeline optimization, pushDownComputedMetaProjection() tracks
* which DocumentSource* stages it has already processed to prevent infinite loops.
* The tracker is a std::vector<DocumentSource*> (_triedComputedMetaPushDownFor).
* Each call does std::find(...) — O(n) pointer comparison — before pushing back.
* As the optimization loop processes more stages, the vector grows and each check
* becomes more expensive. Total: O(S²) where S = stages processed.
*
* Site: document_source_internal_unpack_bucket.h:457
* std::vector<DocumentSource*> _triedComputedMetaPushDownFor;
*
* Site: document_source_internal_unpack_bucket.cpp:1076
* if (std::find(_triedComputedMetaPushDownFor.begin(),
* _triedComputedMetaPushDownFor.end(),
* nextTransform) != _triedComputedMetaPushDownFor.end())
* return boost::none;
* _triedComputedMetaPushDownFor.push_back(nextTransform);
*
* Fix: Replace vector with absl::flat_hash_set<DocumentSource*> (or
* std::unordered_set<DocumentSource*>). Pointer identity is correct here —
* each DocumentSource* is a unique stage object. No custom hash required.
* O(1) lookup replaces O(n) scan.
*
* Comparison counts:
* 0001 defective: N * M (N idFields × M monotonicFields)
* 0001 fixed: N + M (build map once, then M O(1) lookups)
* 0002 defective: 0+1+2+...+(S-1) = S*(S-1)/2 (triangular — each new stage
* scans all previously processed stages)
* 0002 fixed: S (one O(1) hash lookup per stage)
*/
public class MongodbPipelineAlgorithm {
// ── 0001: Streaming group field index resolution ──────────────────────────
public static final class FieldIndexResult {
public final List<Integer> indexes; // resolved positions
public final long comparisons;
public FieldIndexResult(List<Integer> indexes, long comparisons) {
this.indexes = indexes;
this.comparisons = comparisons;
}
}
/** Defective: O(N × M) — std::find scan per monotonic field. */
public static FieldIndexResult defectiveResolveIndexes(
List<String> idFieldNames, List<String> monotonicIdFields) {
List<Integer> indexes = new ArrayList<>();
long comparisons = 0;
for (String fieldName : monotonicIdFields) {
// std::find: scan idFieldNames from beginning
for (int i = 0; i < idFieldNames.size(); i++) {
comparisons++;
if (idFieldNames.get(i).equals(fieldName)) {
indexes.add(i);
break;
}
}
}
Collections.sort(indexes);
return new FieldIndexResult(indexes, comparisons);
}
/** Fixed: O(N + M) — HashMap built once, O(1) per monotonic field lookup. */
public static FieldIndexResult fixedResolveIndexes(
List<String> idFieldNames, List<String> monotonicIdFields) {
List<Integer> indexes = new ArrayList<>();
long comparisons = idFieldNames.size(); // cost of building the map
// Build reverse map once — O(N)
Map<String, Integer> nameToIndex = new HashMap<>();
for (int i = 0; i < idFieldNames.size(); i++) {
nameToIndex.put(idFieldNames.get(i), i);
}
// O(1) per monotonic field
for (String fieldName : monotonicIdFields) {
comparisons++;
Integer idx = nameToIndex.get(fieldName);
if (idx != null) indexes.add(idx);
}
Collections.sort(indexes);
return new FieldIndexResult(indexes, comparisons);
}
// ── 0002: Optimization loop stage tracking ────────────────────────────────
//
// Models pushDownComputedMetaProjection() loop.
// Each "stage" is an integer ID (representing a DocumentSource* pointer).
// The optimization processes S stages; each must be checked against the
// already-processed set before being added.
public static final class OptimizationResult {
public final Set<Integer> processed; // set of processed stage IDs
public final long comparisons;
public OptimizationResult(Set<Integer> processed, long comparisons) {
this.processed = processed;
this.comparisons = comparisons;
}
}
/** Defective: std::vector<DocumentSource*> — O(n) scan per stage. */
public static OptimizationResult defectiveTrackStages(List<Integer> stages) {
List<Integer> tried = new ArrayList<>();
long comparisons = 0;
for (int stage : stages) {
// std::find: scan tried list for pointer equality
boolean found = false;
for (int t : tried) {
comparisons++;
if (t == stage) { found = true; break; }
}
if (!found) tried.add(stage);
}
return new OptimizationResult(new HashSet<>(tried), comparisons);
}
/** Fixed: std::unordered_set<DocumentSource*> — O(1) per stage. */
public static OptimizationResult fixedTrackStages(List<Integer> stages) {
Set<Integer> tried = new HashSet<>();
long comparisons = 0;
for (int stage : stages) {
comparisons++; // O(1) hash lookup
tried.add(stage);
}
return new OptimizationResult(tried, comparisons);
}
// ── Self-test ─────────────────────────────────────────────────────────────
public static void main(String[] args) {
System.out.println("MongodbPipelineAlgorithm — mongodb-0001 + mongodb-0002");
System.out.println();
// 0001: field index resolution
System.out.println("── mongodb-0001: streaming group field index resolution ──");
System.out.println("Defect: std::find on idFieldNames vector — O(N×M)");
System.out.println("Fix: HashMap<field→index> built once — O(N+M)");
System.out.println();
System.out.printf("%-8s %-8s %-14s %-10s %s%n",
"N fields", "M mono", "Defective ops", "Fixed ops", "Speedup");
System.out.println("─".repeat(56));
for (int n : new int[]{5, 10, 20, 50, 100, 200}) {
List<String> idFields = new ArrayList<>();
for (int i = 0; i < n; i++) idFields.add("field_" + i);
// monotonic = last half of id fields (worst case: found at end)
List<String> mono = new ArrayList<>(idFields.subList(n / 2, n));
FieldIndexResult def = defectiveResolveIndexes(idFields, mono);
FieldIndexResult fix = fixedResolveIndexes(idFields, mono);
assert def.indexes.equals(fix.indexes) : "index mismatch at N=" + n;
System.out.printf("%-8d %-8d %-14d %-10d %.1fx%n",
n, mono.size(), def.comparisons, fix.comparisons,
(double) def.comparisons / Math.max(1, fix.comparisons));
}
System.out.println();
// 0002: optimization loop tracking
System.out.println("── mongodb-0002: optimization stage tracker ──");
System.out.println("Defect: std::vector<DocumentSource*> + std::find — O(S²)");
System.out.println("Fix: std::unordered_set<DocumentSource*> — O(S)");
System.out.println();
System.out.printf("%-10s %-14s %-10s %s%n",
"S stages", "Defective ops", "Fixed ops", "Speedup");
System.out.println("─".repeat(46));
Random rng = new Random(42);
for (int s : new int[]{10, 20, 50, 100, 200, 500, 1000}) {
// Mix of unique stages + some revisits (duplicates)
List<Integer> stages = new ArrayList<>();
for (int i = 0; i < s; i++) stages.add(i);
for (int i = 0; i < s / 4; i++) stages.add(rng.nextInt(s)); // revisits
Collections.shuffle(stages, rng);
OptimizationResult def = defectiveTrackStages(stages);
OptimizationResult fix = fixedTrackStages(stages);
assert def.processed.equals(fix.processed) : "processed set mismatch at S=" + s;
System.out.printf("%-10d %-14d %-10d %.1fx%n",
stages.size(), def.comparisons, fix.comparisons,
(double) def.comparisons / Math.max(1, fix.comparisons));
}
}
}