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.*;
/**
* PostgresqlVarDedupAlgorithm models preptlist.c sites postgresql-0002.
*
* DEFECT (CWE-407): tlist_member() walks the entire target list calling
* structural equal() on each entry to check if a Var is already present.
* As the list grows, each check takes longer. Building a tlist of N unique
* Vars from a pool of M candidates costs O(M × N) quadratic when M N.
*
* Sites in PostgreSQL source (preptlist.c):
* :180 MERGE action targetList/qual Var collection
* :206 mergeJoinCondition Var collection
* :316 RETURNING clause Var collection
*
* Defective C pattern (all three sites identical):
*
* vars = pull_var_clause(..., PVC_INCLUDE_PLACEHOLDERS);
* foreach(l, vars) {
* Var *var = (Var *) lfirst(l);
* if (IsA(var, Var) && var->varno == result_relation)
* continue; // skip target-rel vars
* if (tlist_member((Expr *) var, tlist)) // O(n) THE DEFECT
* continue;
* tlist = lappend(tlist, makeTargetEntry(...));
* }
*
* Fix (Path B no nodeHash() required):
* Maintain a HashSet<Var> (C: Bitmapset keyed on encoded varno×varattno)
* alongside the tlist. Check/update the set instead of scanning tlist.
* Reduces per-var check from O(n) to O(1). Total: O(n) instead of O(n²).
*
* C encoding: bms_member(varno * 3200 + varattno + 1600)
* Safe for varno 65001 (INNER_VAR) and varattno in [-1600, 1600].
*
* Comparison counts:
* defective: 0+1+2+...+(N-1) = N*(N-1)/2 (N unique vars, each checked
* against all previously added entries)
* fixed: N (one O(1) hash check per var)
*
* Growth when N doubles: defective 4× (quadratic), fixed 2× (linear).
*/
public class PostgresqlVarDedupAlgorithm {
// Var: models PostgreSQL Var node
//
// A Var represents a column reference: (varno=relation, varattno=column,
// varlevelsup=query nesting depth). PostgreSQL equal() on Vars compares
// all three fields plus vartype and several others. We model the minimum
// set needed to reproduce the defect: varno + varattno + varlevelsup.
public static final class Var {
public final int varno; // relation index (1-based)
public final int varattno; // column number (negative = system col)
public final int varlevelsup; // query nesting (0 = current query)
public Var(int varno, int varattno, int varlevelsup) {
this.varno = varno;
this.varattno = varattno;
this.varlevelsup = varlevelsup;
}
/** Structural equality — models PostgreSQL equal() for Var nodes. */
@Override
public boolean equals(Object o) {
if (!(o instanceof Var v)) return false;
return varno == v.varno && varattno == v.varattno
&& varlevelsup == v.varlevelsup;
}
@Override
public int hashCode() { return Objects.hash(varno, varattno, varlevelsup); }
@Override
public String toString() {
return "Var(" + varno + "," + varattno + "," + varlevelsup + ")";
}
}
// TargetEntry: wraps an expression in the target list
public static final class TargetEntry {
public final Var expr;
public final int resno; // position in tlist (1-based)
public TargetEntry(Var expr, int resno) {
this.expr = expr;
this.resno = resno;
}
}
// Result
public static final class Result {
public final List<TargetEntry> tlist;
/** Number of element-level equality checks performed. */
public final long comparisons;
public Result(List<TargetEntry> tlist, long comparisons) {
this.tlist = tlist;
this.comparisons = comparisons;
}
}
// Defective: tlist_member scan O(n) per var
//
// Exact port of the defective PostgreSQL pattern.
// Each call to tlist_member() walks the existing tlist, calling equal()
// on each entry's expr until it finds a match or exhausts the list.
public static Result defectiveBuildTlist(List<Var> vars, int resultRelation) {
List<TargetEntry> tlist = new ArrayList<>();
long comparisons = 0;
for (Var var : vars) {
if (var.varno == resultRelation)
continue; // skip: belongs to target relation
// tlist_member: scan tlist, call equal() on each expr
boolean found = false;
for (TargetEntry te : tlist) {
comparisons++;
if (te.expr.equals(var)) {
found = true;
break;
}
}
if (found) continue;
tlist.add(new TargetEntry(var, tlist.size() + 1));
}
return new Result(tlist, comparisons);
}
// Fixed: HashSet on Var identity O(1) per var
//
// Maintain a HashSet<Var> alongside the tlist.
// C equivalent: Bitmapset keyed on (varno * 3200 + varattno + 1600).
// Does NOT require nodeHash() Var identity is three plain integers.
public static Result fixedBuildTlist(List<Var> vars, int resultRelation) {
List<TargetEntry> tlist = new ArrayList<>();
Set<Var> seen = new HashSet<>();
long comparisons = 0;
for (Var var : vars) {
if (var.varno == resultRelation)
continue;
comparisons++; // one O(1) hash table lookup
if (seen.contains(var)) continue;
tlist.add(new TargetEntry(var, tlist.size() + 1));
seen.add(var);
}
return new Result(tlist, comparisons);
}
// Self-test
public static void main(String[] args) {
System.out.println("PostgresqlVarDedupAlgorithm — postgresql-0002 (preptlist.c)");
System.out.println("Defect: tlist_member O(n) check per var while building junk tlist");
System.out.println("Fix: HashSet<Var> / Bitmapset — O(1) per check");
System.out.println();
// Simulate a MERGE with multiple actions referencing many columns.
// resultRelation=1, source table=2, many columns referenced.
int[] sizes = {10, 20, 50, 100, 200, 500};
System.out.printf("%-8s %-14s %-10s %-10s %s%n",
"N vars", "Defective ops", "Fixed ops", "Speedup", "Tlist size");
System.out.println("".repeat(62));
for (int n : sizes) {
List<Var> vars = buildVarPool(n, /*resultRelation=*/1, /*duplicates=*/n / 2);
Result def = defectiveBuildTlist(vars, 1);
Result fix = fixedBuildTlist(vars, 1);
// Sanity: both versions must produce the same tlist
assert def.tlist.size() == fix.tlist.size()
: "tlist size mismatch at N=" + n;
System.out.printf("%-8d %-14d %-10d %-10.1fx %d%n",
vars.size(),
def.comparisons,
fix.comparisons,
(double) def.comparisons / Math.max(1, fix.comparisons),
def.tlist.size());
}
System.out.println();
System.out.println("Exact defective count = N*(N-1)/2 (triangular number).");
System.out.println("Fixed count = N (one hash lookup per unique var).");
}
/** Builds a pool of vars mixing unique entries and duplicates. */
public static List<Var> buildVarPool(int uniqueVars, int resultRelation, int extraDuplicates) {
List<Var> pool = new ArrayList<>();
// unique vars from relation 2
for (int att = 1; att <= uniqueVars; att++) {
pool.add(new Var(2, att, 0));
}
// duplicates (already in pool trigger the O(n) scan)
Random rng = new Random(42);
for (int i = 0; i < extraDuplicates; i++) {
int att = rng.nextInt(uniqueVars) + 1;
pool.add(new Var(2, att, 0));
}
// result relation vars (filtered out by both versions)
for (int att = 1; att <= 5; att++) {
pool.add(new Var(resultRelation, att, 0));
}
Collections.shuffle(pool, rng);
return pool;
}
}