java-topology/tests/support/PostgresqlExprMembershipAlgorithm.java
russell@unturf.com 0a580b313d 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.
2026-03-26 17:11:57 -04:00

224 lines
8.6 KiB
Java
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package support;
import java.util.*;
/**
* PostgresqlExprMembershipAlgorithm — models equivclass.c site postgresql-0003.
*
* DEFECT (CWE-407): list_member() is called inside a nested loop to check
* whether each Var pulled from an equivalence-class member expression is
* present in a pre-built list (exprvars). The outer list (exprvars) is fixed
* for the duration of the scan, but rebuilt from scratch via list_member()
* on every inner-loop iteration — O(|exprvars|) per emvar, O(|exprvars| ×
* sum(|emvars|)) total.
*
* Site in PostgreSQL source:
* equivclass.c:1041 — find_em_expr_for_rel(), inner membership check
*
* Defective C pattern:
*
* exprvars = pull_var_clause(some_expr, ...); // fixed for this call
* foreach(lc, ec->ec_members) {
* EquivalenceMember *em = ...;
* emvars = pull_var_clause(em->em_expr, ...); // varies per member
* foreach(lc2, emvars) {
* if (!list_member(exprvars, lfirst(lc2))) // O(|exprvars|) — DEFECT
* break;
* }
* }
*
* Fix: Convert exprvars to a HashSet<Expr> once before the outer loop.
* Each list_member() call becomes O(1). Total cost drops from
* O(|exprvars| × sum(|emvars|)) to O(|exprvars| + sum(|emvars|)).
*
* C equivalent: If emvars are pure Var nodes (common case), use a
* Bitmapset keyed on (varno * 3200 + varattno + 1600) — no nodeHash()
* needed. For non-Var nodes, fall back to list_member().
*
* Comparison counts (M ec_members, each with K emvars, exprvars size E):
* defective: M × K × E (K checks per member, each scanning E exprvars)
* fixed: E + M × K (E to build HashSet, then 1 per emvar check)
*
* Growth when E doubles: defective scales linearly with E, fixed does not.
*/
public class PostgresqlExprMembershipAlgorithm {
// ── Expr: models a PostgreSQL expression node ─────────────────────────────
//
// In real PostgreSQL, expressions are tree nodes compared via equal().
// We model them as integer IDs for clarity; equals()/hashCode() are
// based on the ID, analogous to structural equal() on two Var nodes
// with the same (varno, varattno, varlevelsup).
public static final class Expr {
public final int id; // unique expression identity (encodes Var fields)
public Expr(int id) { this.id = id; }
/** Structural equality — models PostgreSQL equal() for Var nodes. */
@Override
public boolean equals(Object o) {
return o instanceof Expr e && id == e.id;
}
@Override
public int hashCode() { return Integer.hashCode(id); }
@Override
public String toString() { return "Expr(" + id + ")"; }
}
// ── EquivalenceMember: models EquivalenceMember with em_expr ─────────────
public static final class EquivalenceMember {
public final Expr emExpr;
public final List<Expr> emVars; // pull_var_clause(em_expr) result
public EquivalenceMember(Expr emExpr, List<Expr> emVars) {
this.emExpr = emExpr;
this.emVars = emVars;
}
}
// ── Result ────────────────────────────────────────────────────────────────
public static final class Result {
/** The first EquivalenceMember whose emvars are all present in exprvars. */
public final EquivalenceMember found;
/** Total element-level equality checks performed. */
public final long comparisons;
public Result(EquivalenceMember found, long comparisons) {
this.found = found;
this.comparisons = comparisons;
}
}
// ── Defective: list_member scan — O(|exprvars|) per emvar ────────────────
public static Result defectiveFindEm(
List<Expr> exprvars,
List<EquivalenceMember> ecMembers) {
long comparisons = 0;
for (EquivalenceMember em : ecMembers) {
boolean allPresent = true;
for (Expr emvar : em.emVars) {
// list_member: scan exprvars calling equal() on each element
boolean found = false;
for (Expr ev : exprvars) {
comparisons++;
if (ev.equals(emvar)) {
found = true;
break;
}
}
if (!found) {
allPresent = false;
break;
}
}
if (allPresent)
return new Result(em, comparisons);
}
return new Result(null, comparisons);
}
// ── Fixed: HashSet<Expr> from exprvars — O(1) per emvar ─────────────────
//
// Build a HashSet from exprvars once before the outer loop.
// C equivalent: Bitmapset for Var nodes; fall back to list_member for
// non-Var nodes (PlaceHolderVar, Aggref, etc.).
public static Result fixedFindEm(
List<Expr> exprvars,
List<EquivalenceMember> ecMembers) {
// Build HashSet once — O(|exprvars|)
Set<Expr> exprSet = new HashSet<>(exprvars);
long comparisons = exprvars.size(); // cost of building the set
for (EquivalenceMember em : ecMembers) {
boolean allPresent = true;
for (Expr emvar : em.emVars) {
comparisons++; // O(1) hash lookup
if (!exprSet.contains(emvar)) {
allPresent = false;
break;
}
}
if (allPresent)
return new Result(em, comparisons);
}
return new Result(null, comparisons);
}
// ── Self-test ─────────────────────────────────────────────────────────────
public static void main(String[] args) {
System.out.println("PostgresqlExprMembershipAlgorithm — postgresql-0003 (equivclass.c:1041)");
System.out.println("Defect: list_member O(n) scan inside nested EC member loop");
System.out.println("Fix: HashSet<Expr> built once from exprvars — O(1) per check");
System.out.println();
int[] exprvarSizes = {10, 50, 100, 500, 1000};
int emMembers = 20, emVarsPerMember = 5;
System.out.printf("%-12s %-16s %-12s %s%n",
"|exprvars|", "Defective ops", "Fixed ops", "Speedup");
System.out.println("".repeat(56));
Random rng = new Random(42);
for (int exprSize : exprvarSizes) {
List<Expr> exprvars = buildExprList(exprSize, 0);
List<EquivalenceMember> members = buildEcMembers(
emMembers, emVarsPerMember, exprSize, rng);
Result def = defectiveFindEm(exprvars, members);
Result fix = fixedFindEm(exprvars, members);
assert Objects.equals(
def.found != null ? def.found.emExpr : null,
fix.found != null ? fix.found.emExpr : null)
: "result mismatch at |exprvars|=" + exprSize;
System.out.printf("%-12d %-16d %-12d %.1fx%n",
exprSize,
def.comparisons,
fix.comparisons,
(double) def.comparisons / Math.max(1, fix.comparisons));
}
System.out.println();
System.out.println("Defective: O(|exprvars| × M × K) — scales with exprvars size.");
System.out.println("Fixed: O(|exprvars| + M × K) — exprvars built once.");
}
public static List<Expr> buildExprList(int size, int offset) {
List<Expr> list = new ArrayList<>(size);
for (int i = 0; i < size; i++) list.add(new Expr(offset + i));
return list;
}
public static List<EquivalenceMember> buildEcMembers(
int count, int emVarsEach, int exprSize, Random rng) {
List<EquivalenceMember> members = new ArrayList<>();
for (int i = 0; i < count; i++) {
List<Expr> emVars = new ArrayList<>();
for (int j = 0; j < emVarsEach; j++) {
// Half the time pick from exprvars (found), half outside (not found)
int id = rng.nextBoolean() ? rng.nextInt(exprSize) : exprSize + rng.nextInt(10);
emVars.add(new Expr(id));
}
members.add(new EquivalenceMember(new Expr(exprSize + 100 + i), emVars));
}
return members;
}
}