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:
commit
0a580b313d
70422 changed files with 17213626 additions and 0 deletions
119
tests/support/ClosureAlgorithm.java
Normal file
119
tests/support/ClosureAlgorithm.java
Normal file
|
|
@ -0,0 +1,119 @@
|
|||
package support;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Reference implementations of InferenceGraph.Node.closure() — defective and fixed.
|
||||
*
|
||||
* DEFECT 0002b (Infer.java:1747): closure() recomputes the DFS reachability
|
||||
* set on every call — no caching. Called K times from canInfluence() within
|
||||
* the O(N²) loop in buildStuckGraph(). With the O(N) findNode scan (0002a),
|
||||
* the combined total = O(N³).
|
||||
*
|
||||
* Fix: cache the closure result after the first computation.
|
||||
*
|
||||
* Exact node-visit counts when calling closure(N0) K times on a V-node chain:
|
||||
* defective: K * V (each of K calls runs a full O(V) DFS)
|
||||
* fixed: V + (K - 1) (first call = V visits; each cache hit = 1)
|
||||
*
|
||||
* Derivation:
|
||||
* Chain N0→N1→...→N(V-1). closure(N0) = all V nodes.
|
||||
* Defective: K independent DFS traversals each visiting V nodes = K*V.
|
||||
* Fixed: 1st call DFS visits V nodes and stores result. Calls 2..K each
|
||||
* find the cached set in one check: 1 visit each. Total = V + (K-1).
|
||||
*
|
||||
* Growth when K doubles (V fixed): defective ≈2x (linear in K), fixed ≈1x
|
||||
* (nearly constant: (V+2K-1)/(V+K-1) → 1 as V grows or K≪V).
|
||||
*/
|
||||
public class ClosureAlgorithm {
|
||||
|
||||
public static class Node {
|
||||
public final String label;
|
||||
public final List<Node> deps = new ArrayList<>();
|
||||
// used by fixed version
|
||||
Set<Node> cachedClosure = null;
|
||||
|
||||
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 {
|
||||
/** The closure set returned on each of the K calls (all identical). */
|
||||
public final Set<Node> closure;
|
||||
/**
|
||||
* Total node visits across all K calls.
|
||||
* Defective: K * V. Fixed: V + (K-1).
|
||||
*/
|
||||
public final long nodeVisits;
|
||||
Result(Set<Node> closure, long nodeVisits) {
|
||||
this.closure = closure;
|
||||
this.nodeVisits = nodeVisits;
|
||||
}
|
||||
}
|
||||
|
||||
// ─── DEFECTIVE: uncached DFS on every call ───────────────────────────────
|
||||
// Mirrors Infer.java:1747 — computes closure fresh each time it is called.
|
||||
|
||||
public static Result closureDefective(Node start, int calls) {
|
||||
long[] visits = {0};
|
||||
Set<Node> last = null;
|
||||
for (int i = 0; i < calls; i++) {
|
||||
Set<Node> closure = new HashSet<>();
|
||||
dfs(start, closure, visits);
|
||||
last = closure;
|
||||
}
|
||||
return new Result(last, visits[0]);
|
||||
}
|
||||
|
||||
private static void dfs(Node n, Set<Node> visited, long[] visits) {
|
||||
if (visited.contains(n)) return;
|
||||
visits[0]++;
|
||||
visited.add(n);
|
||||
for (Node dep : n.deps) dfs(dep, visited, visits);
|
||||
}
|
||||
|
||||
// ─── FIXED: cached DFS ───────────────────────────────────────────────────
|
||||
// First call runs the DFS and stores the result on the node.
|
||||
// Subsequent calls find the cached result in one check.
|
||||
|
||||
public static Result closureFixed(Node start, int calls) {
|
||||
long[] visits = {0};
|
||||
Set<Node> last = null;
|
||||
for (int i = 0; i < calls; i++) {
|
||||
last = closureCached(start, visits);
|
||||
}
|
||||
return new Result(last, visits[0]);
|
||||
}
|
||||
|
||||
private static Set<Node> closureCached(Node n, long[] visits) {
|
||||
if (n.cachedClosure != null) {
|
||||
visits[0]++; // one operation: read the cached field
|
||||
return n.cachedClosure;
|
||||
}
|
||||
Set<Node> closure = new HashSet<>();
|
||||
dfs(n, closure, visits); // counts V visits
|
||||
n.cachedClosure = closure;
|
||||
return closure;
|
||||
}
|
||||
|
||||
// ─── Factory ─────────────────────────────────────────────────────────────
|
||||
|
||||
/**
|
||||
* Linear chain of V nodes: N0→N1→...→N(V-1).
|
||||
* closure(N0) = all V nodes.
|
||||
* Returns N0 (the start node for closure queries).
|
||||
*
|
||||
* Creates fresh nodes — call this separately for defective vs fixed tests
|
||||
* so the fixed cache does not leak between test runs.
|
||||
*/
|
||||
public static 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.get(0);
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue