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,147 @@
package support;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Deque;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
/**
* Reference implementations of ModuleHashesBuilder$TopoSorter.visit() defective and fixed.
*
* DEFECT 0003 (java.base/jdk.internal.module): visit() checks
* Deque.contains(node) to detect whether the current node is on the DFS
* recursion stack (cycle detection). ArrayDeque.contains() = O(N) linear scan.
* This code lives in java.base present in every JDK/JRE.
*
* Fix: maintain a HashSet<Node> onStack alongside the Deque.
* HashSet.contains() = O(1).
*
* Exact comparison counts for a linear chain of V nodes (N0N1...N(V-1)):
* defective: V*(V-1)/2 (visit(N_k) scans k-element stack = k comparisons;
* sum(k, k=0..V-1) = V*(V-1)/2)
* fixed: V (1 per visit one HashSet.contains() call)
*
* Derivation of defective count:
* visit(N0): stack=[], deque.contains(N0) scans 0 elements 0 comparisons
* visit(N1): stack=[N0], contains(N1) scans 1 element 1 comparison
* visit(N2): stack=[N1,N0], contains(N2) scans 2 elements 2 comparisons
* ...
* visit(N(V-1)): stack is V-1 deep V-1 comparisons
* Total: 0+1+2+...+(V-1) = V*(V-1)/2.
*
* Growth when V doubles: defective 4x (quadratic), fixed 2x (linear).
*/
public class TopoSorterAlgorithm {
public static class Node {
public final String label;
public final List<Node> deps = new ArrayList<>();
public Node(String label) { this.label = label; }
public void addDep(Node dep) { deps.add(dep); }
@Override public String toString() { return label; }
}
public static class Result {
public final List<Node> sorted;
public final long comparisons;
Result(List<Node> sorted, long comparisons) {
this.sorted = sorted;
this.comparisons = comparisons;
}
}
// DEFECTIVE: Deque.contains() for on-stack check
// Mirrors ModuleHashesBuilder$TopoSorter.visit() uses Deque.contains(m)
// to check whether the current node is already on the recursion stack.
public static Result topoSortDefective(List<Node> nodes) {
return new DefectiveSorter().sort(nodes);
}
private static class DefectiveSorter {
private final Deque<Node> stack = new ArrayDeque<>();
private final Set<Node> visited = new HashSet<>();
private final List<Node> sorted = new ArrayList<>();
private long comparisons = 0;
Result sort(List<Node> nodes) {
for (Node n : nodes) {
if (!visited.contains(n)) visit(n);
}
return new Result(sorted, comparisons);
}
void visit(Node v) {
// DEFECT: O(N) scan of the recursion stack for cycle detection
comparisons += dequeContains(stack, v);
visited.add(v);
stack.push(v);
for (Node dep : v.deps) {
if (!visited.contains(dep)) visit(dep);
}
stack.pop();
sorted.add(0, v);
}
private static long dequeContains(Deque<Node> deque, Node target) {
long count = 0;
for (Node n : deque) {
count++;
if (n == target) break;
}
return count;
}
}
// FIXED: HashSet<Node> for on-stack check
// Maintain a dedicated onStack set. HashSet.contains() = O(1).
public static Result topoSortFixed(List<Node> nodes) {
return new FixedSorter().sort(nodes);
}
private static class FixedSorter {
private final Set<Node> onStack = new HashSet<>();
private final Set<Node> visited = new HashSet<>();
private final List<Node> sorted = new ArrayList<>();
private long comparisons = 0;
Result sort(List<Node> nodes) {
for (Node n : nodes) {
if (!visited.contains(n)) visit(n);
}
return new Result(sorted, comparisons);
}
void visit(Node v) {
comparisons++; // O(1) HashSet.contains() for cycle check
// onStack.contains(v) would be false here (not a cycle); add anyway
visited.add(v);
onStack.add(v);
for (Node dep : v.deps) {
if (!visited.contains(dep)) visit(dep);
}
onStack.remove(v);
sorted.add(0, v);
}
}
// Factory
/**
* Linear chain: N0N1...N(V-1). No cycles a valid DAG for topo sort.
* DFS visits nodes in stack-depth order, producing an increasing stack depth
* at each level.
*
* Defective comparisons: V*(V-1)/2
* Fixed comparisons: V
*/
public static List<Node> buildLinearChain(int v) {
List<Node> nodes = new ArrayList<>();
for (int i = 0; i < v; i++) nodes.add(new Node("N" + i));
for (int i = 0; i < v - 1; i++) nodes.get(i).addDep(nodes.get(i + 1));
return nodes;
}
}