distlib/luigi/zookeeper/mybatis/solc/solana/go-ethereum: CWE-407 patches + unit tests; solana CLEAN

This commit is contained in:
russell@unturf.com 2026-03-30 07:06:26 -04:00
parent c9e86c450c
commit b4e2eda927
17 changed files with 2087 additions and 1 deletions

View file

@ -0,0 +1,21 @@
# UNDF: UNDF-2026-000000046
--- a/distlib/database.py
+++ b/distlib/database.py
@@ -1277,10 +1277,13 @@ def get_dependent_dists(dists, dist):
graph = make_graph(dists)
- dep = [dist] # dependent distributions
+ dep = [dist] # dependent distributions (ordered output list)
+ dep_set = {dist} # O(1) membership mirror of dep
todo = graph.reverse_list[dist] # list of nodes we should inspect
while todo:
d = todo.pop()
dep.append(d)
+ dep_set.add(d)
for succ in graph.reverse_list[d]:
- if succ not in dep:
+ if succ not in dep_set: # O(1) instead of O(N) list scan
todo.append(succ)
dep.pop(0) # remove dist from dep, was there to prevent infinite loops

View file

@ -0,0 +1,39 @@
# UNDF: UNDF-2026-000000722
--- a/distlib/util.py
+++ b/distlib/util.py
@@ -1,6 +1,6 @@
import codecs
-from collections import deque
+from collections import deque, OrderedDict
import contextlib
@@ -1127,16 +1127,16 @@ def get_steps(self, final):
if not self.is_step(final):
raise ValueError('Unknown: %r' % final)
- result = []
- todo = []
+ result = OrderedDict() # preserves insertion order, O(1) move_to_end
+ todo = deque() # O(1) popleft instead of O(N) list.pop(0)
seen = set()
- todo.append(final)
+ todo.append(final) # deque.append is O(1)
while todo:
- step = todo.pop(0)
+ step = todo.popleft() # O(1) instead of O(N) list.pop(0)
if step in seen:
# if a step was already seen,
# move it to the end (so it will appear earlier
# when reversed on return) ... but not for the
# final step, as that would be confusing for
# users
if step != final:
- result.remove(step) # O(N) list scan
- result.append(step)
+ result.move_to_end(step) # O(1) OrderedDict relink
else:
seen.add(step)
- result.append(step)
+ result[step] = None
preds = self._preds.get(step, ())
todo.extend(preds)
- return reversed(result)
+ return reversed(list(result))

View file

@ -0,0 +1,402 @@
"""
Unit tests for distlib CWE-407 defects.
distlib-0001: get_dependent_dists dep list O() membership check
File: distlib/database.py ~line 1284
Fix: parallel dep_set for O(1) succ-not-in-dep guard
distlib-0002: get_steps result.remove O(N) + todo.pop(0) O(N)
File: distlib/util.py ~line 1127
Fix: OrderedDict.move_to_end O(1) + deque.popleft O(1)
Tests use operation counters to verify algorithmic complexity.
No distlib import needed implementations are inlined as stubs.
"""
import sys
import time
from collections import deque, OrderedDict
# ---------------------------------------------------------------------------
# distlib-0001: get_dependent_dists
# ---------------------------------------------------------------------------
def get_dependent_dists_before(reverse_list, dist):
"""
Defective implementation: O() due to `succ not in dep` list scan.
Returns (result_list, op_count).
"""
ops = 0
dep = [dist]
todo = list(reverse_list.get(dist, []))
while todo:
d = todo.pop()
dep.append(d)
for succ in reverse_list.get(d, []):
ops += len(dep) # cost of `succ not in dep`
if succ not in dep:
todo.append(succ)
dep.pop(0)
return dep, ops
def get_dependent_dists_after(reverse_list, dist):
"""
Fixed implementation: O(N) with parallel dep_set for O(1) membership.
Returns (result_list, op_count).
"""
ops = 0
dep = [dist]
dep_set = {dist}
todo = list(reverse_list.get(dist, []))
while todo:
d = todo.pop()
dep.append(d)
dep_set.add(d)
for succ in reverse_list.get(d, []):
ops += 1 # O(1) set lookup
if succ not in dep_set:
todo.append(succ)
dep.pop(0)
return dep, ops
def make_fan_in_graph(n):
"""
Build a reverse_list that forces O() membership checks.
Structure: a "collector" node c is depended on by p0..p(N-1) (in
reverse_list terms: reverse_list[c] = [p0..p(N-1)]).
Each pi is in turn depended on by a unique "leaf" node li
(reverse_list[pi] = [li]).
Additionally, every leaf li lists *all* pj (j!=i) as its successors,
so when we visit li, we check all N pj nodes against dep.
By the time we visit leaf li, dep already contains c, p0..p(i), and
the previously-visited leaves, so each `pj not in dep` check runs
against a growing list.
Total membership checks: N leaves × N pj nodes = O().
All checks correctly return False the first time (pj nodes added from
c's reverse_list are in dep) or True when pj is already there.
Result size = 1 (c) + N (pi) + N (li) = 2N+1 dependents.
"""
reverse_list = {}
# c's direct dependents: p0..p(N-1)
reverse_list["c"] = [f"p{i}" for i in range(n)]
for i in range(n):
# pi is depended on by leaf li
reverse_list[f"p{i}"] = [f"l{i}"]
# leaf li lists all pj as successors (all already in dep when li visited)
reverse_list[f"l{i}"] = [f"p{j}" for j in range(n)]
return reverse_list
def make_simple_chain(n):
"""
Simple linear chain: base <- p0 <- p1 <- ... <- p(n-1).
reverse_list[base] = [p0], reverse_list[pi] = [p(i+1)], last has [].
Result of get_dependent_dists(base) = [p0, p1, ..., p(n-1)].
"""
rl = {"base": ["p0"]}
for i in range(n - 1):
rl[f"p{i}"] = [f"p{i+1}"]
rl[f"p{n-1}"] = []
return rl
def test_distlib_0001_correctness():
"""Both implementations produce the same result on a simple chain."""
N = 20
rl = make_simple_chain(N)
result_before, _ = get_dependent_dists_before(rl, "base")
result_after, _ = get_dependent_dists_after(rl, "base")
assert sorted(result_before) == sorted(result_after), (
f"Results differ:\n before={sorted(result_before)}\n after={sorted(result_after)}"
)
assert len(result_before) == N, (
f"Expected {N} dependents, got {len(result_before)}"
)
print(f" correctness: both return {len(result_before)} dependents")
def test_distlib_0001_complexity():
"""
BEFORE: op count grows as O(); AFTER: op count grows as O(N).
At N=500, each of N leaves checks N peers against a dep list of size ~N
~ = 250,000 membership-check ops in BEFORE; each check is O(1) in
AFTER ~ total ops but each counted as 1 ratio reflects list vs set.
Expected ratio: >= 50x.
"""
N = 500
rl = make_fan_in_graph(N)
_, ops_before = get_dependent_dists_before(rl, "c")
_, ops_after = get_dependent_dists_after(rl, "c")
ratio = ops_before / ops_after if ops_after > 0 else float("inf")
print(f" N={N}: ops_before={ops_before:,}, ops_after={ops_after:,}, ratio={ratio:.1f}x")
assert ratio >= 50, (
f"Expected >= 50x op ratio at N={N}, got {ratio:.1f}x "
f"(before={ops_before:,}, after={ops_after:,})"
)
print(f" PASS: {ratio:.1f}x operation-count reduction")
def test_distlib_0001_no_infinite_loop():
"""
dep / dep_set prevent revisiting nodes in a graph with shared sub-deps.
Diamond: base <- {a, b}, a <- c, b <- c (c depends on both a and b).
"""
reverse_list = {
"base": ["a", "b"],
"a": ["c"],
"b": ["c"],
"c": [],
}
result_before, _ = get_dependent_dists_before(reverse_list, "base")
result_after, _ = get_dependent_dists_after(reverse_list, "base")
# c should appear exactly once in both (dedup prevents double-add)
assert result_before.count("c") == 1, f"BEFORE: c appears {result_before.count('c')} times"
assert result_after.count("c") == 1, f"AFTER: c appears {result_after.count('c')} times"
print(f" diamond dedup: BEFORE={result_before}, AFTER={result_after}")
# ---------------------------------------------------------------------------
# distlib-0002: get_steps
# ---------------------------------------------------------------------------
def get_steps_before(preds, final):
"""
Defective implementation:
- result.remove(step) is O(N) list scan
- todo.pop(0) is O(N) list shift
Returns (result_list, op_count).
"""
ops = 0
result = []
todo = []
seen = set()
todo.append(final)
while todo:
ops += len(todo) # cost of pop(0): shifts all remaining elements
step = todo.pop(0)
if step in seen:
if step != final:
ops += len(result) # cost of result.remove(step)
result.remove(step)
result.append(step)
else:
seen.add(step)
result.append(step)
preds_list = preds.get(step, ())
todo.extend(preds_list)
return list(reversed(result)), ops
def get_steps_after(preds, final):
"""
Fixed implementation:
- result is OrderedDict; move_to_end is O(1)
- todo is deque; popleft is O(1)
Returns (result_list, op_count).
"""
ops = 0
result = OrderedDict()
todo = deque()
seen = set()
todo.append(final)
while todo:
ops += 1 # O(1) deque.popleft
step = todo.popleft()
if step in seen:
if step != final:
ops += 1 # O(1) OrderedDict.move_to_end
result.move_to_end(step)
else:
seen.add(step)
result[step] = None
preds_list = preds.get(step, ())
todo.extend(preds_list)
return list(reversed(list(result))), ops
def make_diamond_steps():
"""
Diamond dependency graph for steps:
final -> a -> c
final -> b -> c
Step c appears as a predecessor of both a and b.
When c is first seen via a, then revisited via b, result.remove(c)
is called in the BEFORE version.
"""
return {
"final": ("a", "b"),
"a": ("c",),
"b": ("c",),
"c": (),
}
def make_chain_steps(n):
"""
Linear chain: final -> s0 -> s1 -> ... -> s(n-1).
Every step is visited exactly once; no remove() calls.
Used to stress-test pop(0) vs popleft().
"""
preds = {"final": (f"s0",)}
for i in range(n - 1):
preds[f"s{i}"] = (f"s{i+1}",)
preds[f"s{n-1}"] = ()
return preds
def make_wide_diamond_steps(width, depth):
"""
Wide diamond: final has `width` direct predecessors, each of which
has the same `depth` common predecessors. Each common predecessor
is revisited `width` times triggering result.remove() in BEFORE.
"""
preds = {}
shared = [f"shared_{d}" for d in range(depth)]
branches = [f"branch_{w}" for w in range(width)]
preds["final"] = tuple(branches)
for b in branches:
preds[b] = tuple(shared)
for s in shared:
preds[s] = ()
return preds, shared
def test_distlib_0002_correctness_diamond():
"""Both implementations produce the same topological order on a diamond."""
preds = make_diamond_steps()
result_before, _ = get_steps_before(preds, "final")
result_after, _ = get_steps_after(preds, "final")
assert result_before == result_after, (
f"Results differ:\n before={result_before}\n after={result_after}"
)
# c should be first (deepest common dep), final last
assert result_before[0] == "c", f"Expected 'c' first, got {result_before[0]}"
assert result_before[-1] == "final", f"Expected 'final' last, got {result_before[-1]}"
print(f" diamond order: {result_before}")
def test_distlib_0002_correctness_chain():
"""Linear chain: order must be [s(n-1), ..., s0, final]."""
N = 20
preds = make_chain_steps(N)
result_before, _ = get_steps_before(preds, "final")
result_after, _ = get_steps_after(preds, "final")
assert result_before == result_after, (
f"Chain results differ:\n before={result_before}\n after={result_after}"
)
expected_first = f"s{N-1}"
assert result_before[0] == expected_first, (
f"Expected '{expected_first}' first, got {result_before[0]}"
)
print(f" chain N={N}: first={result_before[0]}, last={result_before[-1]}")
def test_distlib_0002_complexity():
"""
BEFORE: op count grows as O() for wide-diamond graphs.
AFTER: op count grows as O(N).
Build a graph with width=200 branches all sharing depth=100 common steps.
BEFORE: each of 100 shared nodes is remove()'d 199 times → ~100×200 = 20,000
remove ops, each scanning a result list of up to 300 items ~6,000,000 ops.
AFTER: each move_to_end is O(1) ~200×100 = 20,000 ops total.
Expected ratio: >= 50x.
"""
WIDTH = 200
DEPTH = 100
preds, _ = make_wide_diamond_steps(WIDTH, DEPTH)
_, ops_before = get_steps_before(preds, "final")
_, ops_after = get_steps_after(preds, "final")
ratio = ops_before / ops_after if ops_after > 0 else float("inf")
print(f" width={WIDTH}, depth={DEPTH}: ops_before={ops_before:,}, "
f"ops_after={ops_after:,}, ratio={ratio:.1f}x")
assert ratio >= 50, (
f"Expected >= 50x op ratio, got {ratio:.1f}x "
f"(before={ops_before:,}, after={ops_after:,})"
)
print(f" PASS: {ratio:.1f}x operation-count reduction")
def test_distlib_0002_popleft_complexity():
"""
todo.pop(0) on a list is O(N); deque.popleft() is O(1).
To expose the difference, we need a graph where todo stays large during
traversal. A wide fan-out graph achieves this: final has N direct
predecessors, all leaf nodes. When each branch is popped from todo, the
remaining todo list still holds all the sibling branches pop(0) must
shift them all. At N predecessors the total shift-work is O().
We count shifts explicitly by accumulating len(todo) before each pop(0).
"""
N = 2000
# final -> p0, p1, ..., p(N-1); each pi is a leaf
preds = {"final": tuple(f"p{i}" for i in range(N))}
for i in range(N):
preds[f"p{i}"] = ()
_, ops_before = get_steps_before(preds, "final")
_, ops_after = get_steps_after(preds, "final")
ratio = ops_before / ops_after if ops_after > 0 else float("inf")
print(f" fan-out N={N}: ops_before={ops_before:,}, ops_after={ops_after:,}, ratio={ratio:.1f}x")
assert ratio >= 50, (
f"Expected >= 50x op ratio at N={N}, got {ratio:.1f}x "
f"(before={ops_before:,}, after={ops_after:,})"
)
print(f" PASS: {ratio:.1f}x operation-count reduction")
# ---------------------------------------------------------------------------
# Runner
# ---------------------------------------------------------------------------
TESTS = [
# distlib-0001
test_distlib_0001_correctness,
test_distlib_0001_complexity,
test_distlib_0001_no_infinite_loop,
# distlib-0002
test_distlib_0002_correctness_diamond,
test_distlib_0002_correctness_chain,
test_distlib_0002_complexity,
test_distlib_0002_popleft_complexity,
]
if __name__ == "__main__":
failed = 0
for t in TESTS:
print(f"=== {t.__name__} ===")
try:
t()
print(" PASS")
except AssertionError as e:
print(f" FAIL: {e}", file=sys.stderr)
failed += 1
except Exception as e:
print(f" ERROR: {e}", file=sys.stderr)
failed += 1
print()
if failed:
print(f"FAILED: {failed}/{len(TESTS)} tests failed", file=sys.stderr)
sys.exit(1)
else:
print(f"ALL {len(TESTS)} TESTS PASSED")

View file

@ -0,0 +1,77 @@
# UNDF: UNDF-2026-000000082
# UNDF: (pending assignment)
--- a/core/txpool/legacypool/legacypool.go
+++ b/core/txpool/legacypool/legacypool.go
@@ -1649,7 +1649,7 @@ type lookup struct {
slots int
lock sync.RWMutex
txs map[common.Hash]*types.Transaction
- auths map[common.Address][]common.Hash // All accounts with a pooled authorization
+ auths map[common.Address]map[common.Hash]struct{} // All accounts with a pooled authorization
}
// newLookup returns a new lookup structure.
@@ -1658,7 +1658,7 @@ func newLookup() *lookup {
return &lookup{
txs: make(map[common.Hash]*types.Transaction),
- auths: make(map[common.Address][]common.Hash),
+ auths: make(map[common.Address]map[common.Hash]struct{}),
}
}
@@ -1737,7 +1737,7 @@ func (t *lookup) Clear() {
t.slots = 0
t.txs = make(map[common.Hash]*types.Transaction)
- t.auths = make(map[common.Address][]common.Hash)
+ t.auths = make(map[common.Address]map[common.Hash]struct{})
}
// addAuthorities tracks the supplied tx in relation to each authority it
@@ -1756,13 +1756,12 @@ func (t *lookup) addAuthorities(tx *types.Transaction) {
for _, addr := range tx.SetCodeAuthorities() {
- list, ok := t.auths[addr]
- if !ok {
- list = []common.Hash{}
- }
- if slices.Contains(list, tx.Hash()) {
- // Don't add duplicates.
+ set, ok := t.auths[addr]
+ if !ok {
+ set = make(map[common.Hash]struct{})
+ t.auths[addr] = set
+ }
+ if _, dup := set[tx.Hash()]; dup {
+ // Don't add duplicates.
continue
}
- list = append(list, tx.Hash())
- t.auths[addr] = list
+ set[tx.Hash()] = struct{}{}
}
}
@@ -1773,16 +1772,14 @@ func (t *lookup) removeAuthorities(tx *types.Transaction) {
hash := tx.Hash()
for _, addr := range tx.SetCodeAuthorities() {
- list := t.auths[addr]
- // Remove tx from tracker.
- if i := slices.Index(list, hash); i >= 0 {
- list = append(list[:i], list[i+1:]...)
- } else {
+ set := t.auths[addr]
+ // Remove tx from tracker.
+ if _, ok := set[hash]; ok {
+ delete(set, hash)
+ } else {
log.Error("Authority with untracked tx", "addr", addr, "hash", hash)
}
- if len(list) == 0 {
- // If list is newly empty, delete it entirely.
+ if len(set) == 0 {
+ // If set is newly empty, delete it entirely.
delete(t.auths, addr)
continue
}
- t.auths[addr] = list
}
}

View file

@ -0,0 +1,195 @@
package unit;
import java.util.*;
/**
* GoEthereumTest CWE-407 benchmark for go-ethereum-0001
*
* go-ethereum-0001: txpool/legacypool lookup.addAuthorities() auths slice O(N²)
* Real code (core/txpool/legacypool/legacypool.go):
* auths map[common.Address][]common.Hash // slice per authority
*
* func (t *lookup) addAuthorities(tx *types.Transaction) {
* for _, addr := range tx.SetCodeAuthorities() {
* list, ok := t.auths[addr]
* if !ok { list = []common.Hash{} }
* if slices.Contains(list, tx.Hash()) { // O(N) scan
* continue
* }
* list = append(list, tx.Hash())
* t.auths[addr] = list
* }
* }
*
* When many txs share the same authority address, the slice for that address
* grows to length N and each Contains() call scans the whole list: O(N) per
* insertion, O(N²) total.
*
* Fix: change auths to map[common.Address]map[common.Hash]struct{} O(1) lookup.
*
* func (t *lookup) addAuthorities(tx *types.Transaction) {
* for _, addr := range tx.SetCodeAuthorities() {
* set, ok := t.auths[addr]
* if !ok {
* set = make(map[common.Hash]struct{})
* t.auths[addr] = set
* }
* if _, dup := set[tx.Hash()]; dup { continue }
* set[tx.Hash()] = struct{}{}
* }
* }
*/
public class GoEthereumTest {
// --- Defective implementation: auths as map[addr][]hash (slice) ---
/**
* Simulates lookup.addAuthorities() with a slice per authority address.
*
* @param N number of distinct tx hashes to add under one authority
* @return total comparison operations performed (slices.Contains scans)
*/
static long slowAddAuthorities(int N) {
// auths: map[address][]hash one entry, one authority address
Map<String, List<String>> auths = new HashMap<>();
long ops = 0;
for (int i = 0; i < N; i++) {
String addr = "authority_0";
String hash = "tx_hash_" + i;
List<String> list = auths.computeIfAbsent(addr, k -> new ArrayList<>());
// slices.Contains O(current list length)
boolean dup = false;
for (int j = 0; j < list.size(); j++) {
ops++;
if (list.get(j).equals(hash)) { dup = true; break; }
}
if (!dup) list.add(hash);
}
return ops;
}
/**
* Simulates lookup.addAuthorities() with a set per authority address (the fix).
*
* @param N number of distinct tx hashes to add under one authority
* @return total comparison operations performed (map probes, each O(1))
*/
static long fastAddAuthorities(int N) {
// auths: map[address]map[hash]struct{} O(1) lookup
Map<String, Set<String>> auths = new HashMap<>();
long ops = 0;
for (int i = 0; i < N; i++) {
String addr = "authority_0";
String hash = "tx_hash_" + i;
Set<String> set = auths.computeIfAbsent(addr, k -> new HashSet<>());
ops++; // one O(1) probe (contains + add combined)
set.add(hash);
}
return ops;
}
// --- Benchmark harness ---
static void bench(String label, Runnable slow, Runnable fast, long sOps, long fOps) {
// warm up
slow.run(); fast.run();
long t0 = System.nanoTime(); slow.run(); long sMs = (System.nanoTime() - t0) / 1_000_000;
long t1 = System.nanoTime(); fast.run(); long fMs = (System.nanoTime() - t1) / 1_000_000;
double ratio = (double) sOps / Math.max(fOps, 1);
System.out.printf(" %-55s slow: %5dms (%,d ops) fast: %5dms (%,d ops) op-ratio: %.0fx%n",
label, sMs, sOps, fMs, fOps, ratio);
}
public static void main(String[] args) {
System.out.println("GoEthereumTest — go-ethereum-0001: txpool lookup.addAuthorities() slice O(N²) vs map O(N)");
System.out.println();
System.out.println(" [go-ethereum-0001: addAuthorities() dedup check]");
int[] cases = {100, 500, 1000};
for (int N : cases) {
long sOps = slowAddAuthorities(N);
long fOps = fastAddAuthorities(N);
bench(
String.format("N=%d txs, 1 authority address", N),
() -> slowAddAuthorities(N),
() -> fastAddAuthorities(N),
sOps, fOps
);
}
System.out.println();
// --- Assertions ---
int pass = 0;
// 1. Slice approach is O(N²): total ops for N unique hashes = 0+1+2++(N-1) = N*(N-1)/2
// For N=1000 that's 499,500 ops. Map is N=1000 ops. Ratio ~500x.
{
int N = 1000;
long sOps = slowAddAuthorities(N);
long fOps = fastAddAuthorities(N);
long expected_slow = (long) N * (N - 1) / 2; // 499500
assert sOps == expected_slow
: "go-ethereum-0001 slow op count mismatch: got=" + sOps + " expected=" + expected_slow;
assert fOps == N
: "go-ethereum-0001 fast op count mismatch: got=" + fOps + " expected=" + N;
double ratio = (double) sOps / fOps;
assert ratio > 200
: "go-ethereum-0001 expected >200x op-ratio; got " + ratio;
System.out.printf(" PASS go-ethereum-0001: N=%d, slow=%,d ops, fast=%,d ops, ratio=%.0fx%n",
N, sOps, fOps, ratio);
pass++;
}
// 2. Duplicate insertion: slice and map both reject duplicates, same count
{
// Add same hash twice only one should be retained
Map<String, List<String>> sliceMap = new HashMap<>();
List<String> list = sliceMap.computeIfAbsent("addr", k -> new ArrayList<>());
String hash = "tx_0";
if (!list.contains(hash)) list.add(hash);
if (!list.contains(hash)) list.add(hash);
assert list.size() == 1 : "slice dedup failed: size=" + list.size();
Map<String, Set<String>> setMap = new HashMap<>();
Set<String> set = setMap.computeIfAbsent("addr", k -> new HashSet<>());
set.add(hash);
set.add(hash);
assert set.size() == 1 : "map dedup failed: size=" + set.size();
System.out.printf(" PASS duplicate-rejection: slice.size=%d, set.size=%d%n",
list.size(), set.size());
pass++;
}
// 3. Remove: map supports O(1) delete vs slice's O(N) index scan
// Verify correctness of remove-by-key semantics
{
Map<String, Set<String>> setMap = new HashMap<>();
Set<String> set = setMap.computeIfAbsent("addr", k -> new HashSet<>());
for (int i = 0; i < 5; i++) set.add("tx_" + i);
set.remove("tx_2");
assert !set.contains("tx_2") : "map remove failed";
assert set.size() == 4 : "map remove size wrong: " + set.size();
if (set.isEmpty()) setMap.remove("addr");
set.clear();
setMap.computeIfAbsent("addr", k -> new HashSet<>());
setMap.get("addr").add("tx_x");
setMap.get("addr").remove("tx_x");
if (setMap.get("addr").isEmpty()) setMap.remove("addr");
assert !setMap.containsKey("addr") : "empty-set cleanup failed";
System.out.printf(" PASS remove-authority: O(1) delete, empty-cleanup correct%n");
pass++;
}
System.out.println();
System.out.printf("%d/3 PASS%n", pass);
System.out.printf("go-ethereum-0001: txpool lookup.addAuthorities() auths []hash → map[hash]struct{} O(N²) → O(N)%n");
System.out.printf("Hotpath: every EIP-7702 set-code tx added to pool; worst case O(T×I) per authority%n");
if (pass < 3) {
System.out.println("FAIL");
System.exit(1);
}
System.out.println("ALL PASS");
}
}

View file

@ -0,0 +1,39 @@
# UNDF: UNDF-2026-000000159
--- a/luigi/scheduler.py
+++ b/luigi/scheduler.py
@@ -1288,18 +1288,25 @@ class CentralPlannerScheduler(object):
def _upstream_status(self, task_id, upstream_status_table):
if task_id in upstream_status_table:
return upstream_status_table[task_id]
elif self._state.has_task(task_id):
task_stack = [task_id]
+ in_stack = {task_id} # O(1) membership test; prevents duplicate stack entries
while task_stack:
dep_id = task_stack.pop()
+ in_stack.discard(dep_id)
dep = self._state.get_task(dep_id)
if dep:
if dep.status == DONE:
continue
if dep_id not in upstream_status_table:
if dep.status == PENDING and dep.deps:
- task_stack += [dep_id] + list(dep.deps)
- upstream_status_table[dep_id] = "" # will be updated postorder
+ # re-push dep_id for postorder processing
+ task_stack.append(dep_id)
+ in_stack.add(dep_id)
+ upstream_status_table[dep_id] = "" # will be updated postorder
+ for child_id in dep.deps:
+ if child_id not in upstream_status_table and child_id not in in_stack:
+ task_stack.append(child_id)
+ in_stack.add(child_id)
else:
dep_status = STATUS_TO_UPSTREAM_MAP.get(dep.status, "")
upstream_status_table[dep_id] = dep_status
elif upstream_status_table[dep_id] == "" and dep.deps:
# This is the postorder update step when we set the
# status based on the previously calculated child elements
status = max((upstream_status_table.get(a_task_id, "") for a_task_id in dep.deps), key=UPSTREAM_SEVERITY_KEY)
upstream_status_table[dep_id] = status
return upstream_status_table[dep_id]

View file

@ -0,0 +1,377 @@
"""
CWE-407: Algorithmic Complexity - Luigi scheduler._upstream_status
luigi-0001: _upstream_status missing in_stack guard O(edges) vs O(nodes)
In luigi/scheduler.py lines 1300-1303:
if dep_id not in upstream_status_table:
if dep.status == PENDING and dep.deps:
task_stack += [dep_id] + list(dep.deps) # BUG: no guard
upstream_status_table[dep_id] = ""
When multiple PENDING tasks share downstream dependencies (diamond/DAG
pattern), each parent unconditionally pushes all its children onto
task_stack including children that are already queued or already resolved.
This means every edge in the dependency graph generates a stack entry,
giving O(E) total stack operations where E = number of edges.
In a dense DAG (e.g. complete DAG where node i depends on all nodes j>i),
E = O(N^2), so the algorithm is O(N^2) while the fixed version is O(N).
The fix adds an in_stack set so each child is enqueued at most once,
bounding stack operations to O(N) regardless of edge density.
Measured ratio at N=30: ~8x (defect=527 pops, fixed=63 pops).
Measured ratio at N=50: ~13x (defect=1377 pops, fixed=103 pops).
"""
import sys
PENDING = "PENDING"
DONE = "DONE"
FAILED = "FAILED"
STATUS_TO_UPSTREAM_MAP = {
FAILED: "UPSTREAM_FAILED",
PENDING: "UPSTREAM_PENDING",
}
UPSTREAM_SEVERITY_ORDER = [
"", "UPSTREAM_PENDING", "UPSTREAM_DISABLED",
"UPSTREAM_FAILED", "UPSTREAM_MISSING_INPUT",
]
def UPSTREAM_SEVERITY_KEY(s):
try:
return UPSTREAM_SEVERITY_ORDER.index(s)
except ValueError:
return -1
# ---------------------------------------------------------------------------
# Minimal task/state stubs (mirror luigi internals just enough for simulation)
# ---------------------------------------------------------------------------
class FakeTask:
def __init__(self, task_id, status, deps):
self.id = task_id
self.status = status
self.deps = list(deps)
class FakeState:
def __init__(self, tasks):
self._tasks = {t.id: t for t in tasks}
def has_task(self, task_id):
return task_id in self._tasks
def get_task(self, task_id):
return self._tasks.get(task_id)
# ---------------------------------------------------------------------------
# Faithful simulation of the defective implementation
# (mirrors scheduler.py lines 1288-1312 exactly, injecting a pop counter)
# ---------------------------------------------------------------------------
def upstream_status_defect(task_id, state):
"""
Defective _upstream_status: no in_stack guard.
Returns (upstream_status, pop_count) where pop_count measures total work.
"""
upstream_status_table = {}
if not state.has_task(task_id):
return ("", 0)
task_stack = [task_id]
pop_count = 0
while task_stack:
dep_id = task_stack.pop()
pop_count += 1
dep = state.get_task(dep_id)
if dep:
if dep.status == DONE:
continue
if dep_id not in upstream_status_table:
if dep.status == PENDING and dep.deps:
# BUG: pushes all deps unconditionally — duplicates accumulate
task_stack += [dep_id] + list(dep.deps)
upstream_status_table[dep_id] = ""
else:
dep_status = STATUS_TO_UPSTREAM_MAP.get(dep.status, "")
upstream_status_table[dep_id] = dep_status
elif upstream_status_table[dep_id] == "" and dep.deps:
status = max(
(upstream_status_table.get(a, "") for a in dep.deps),
key=UPSTREAM_SEVERITY_KEY,
)
upstream_status_table[dep_id] = status
return (upstream_status_table.get(dep_id, ""), pop_count)
# ---------------------------------------------------------------------------
# Fixed implementation
# ---------------------------------------------------------------------------
def upstream_status_fixed(task_id, state):
"""
Fixed _upstream_status: in_stack guard prevents duplicate enqueue.
Returns (upstream_status, pop_count).
"""
upstream_status_table = {}
if not state.has_task(task_id):
return ("", 0)
task_stack = [task_id]
in_stack = {task_id}
pop_count = 0
while task_stack:
dep_id = task_stack.pop()
in_stack.discard(dep_id)
pop_count += 1
dep = state.get_task(dep_id)
if dep:
if dep.status == DONE:
continue
if dep_id not in upstream_status_table:
if dep.status == PENDING and dep.deps:
task_stack.append(dep_id)
in_stack.add(dep_id)
upstream_status_table[dep_id] = ""
for child_id in dep.deps:
if child_id not in upstream_status_table and child_id not in in_stack:
task_stack.append(child_id)
in_stack.add(child_id)
else:
dep_status = STATUS_TO_UPSTREAM_MAP.get(dep.status, "")
upstream_status_table[dep_id] = dep_status
elif upstream_status_table[dep_id] == "" and dep.deps:
status = max(
(upstream_status_table.get(a, "") for a in dep.deps),
key=UPSTREAM_SEVERITY_KEY,
)
upstream_status_table[dep_id] = status
return (upstream_status_table.get(dep_id, ""), pop_count)
# ---------------------------------------------------------------------------
# Graph constructors
# ---------------------------------------------------------------------------
def make_complete_dag(n):
"""
Complete DAG of depth n + leaf.
- root (PENDING) depends on [n0, n1, ..., n_{n-1}]
- node n_i (PENDING) depends on [n_{i+1}, ..., n_{n-1}, leaf]
- leaf (FAILED) has no deps
Edge count E = O(N^2): node i has (N-i) outgoing edges.
The defect processes O(E) = O(N^2) stack entries.
The fix processes O(N) stack entries.
"""
tasks = []
node_ids = [f"n{i}" for i in range(n)]
tasks.append(FakeTask("root", PENDING, node_ids))
for i in range(n):
forward_deps = [f"n{j}" for j in range(i + 1, n)] + ["leaf"]
tasks.append(FakeTask(f"n{i}", PENDING, forward_deps))
tasks.append(FakeTask("leaf", FAILED, []))
return FakeState(tasks)
def make_diamond_state(n_parents):
"""
Simple diamond: root (PENDING) -> [p0..p_{N-1}] (PENDING) -> shared (FAILED).
Each parent unconditionally pushes shared; the fix enqueues it once.
"""
tasks = []
parent_ids = [f"p{i}" for i in range(n_parents)]
tasks.append(FakeTask("root", PENDING, parent_ids))
for pid in parent_ids:
tasks.append(FakeTask(pid, PENDING, ["shared"]))
tasks.append(FakeTask("shared", FAILED, []))
return FakeState(tasks)
def make_linear_chain():
"""A -> B -> C -> D(FAILED): no diamonds, both versions behave identically."""
return FakeState([
FakeTask("A", PENDING, ["B"]),
FakeTask("B", PENDING, ["C"]),
FakeTask("C", PENDING, ["D"]),
FakeTask("D", FAILED, []),
])
def make_all_done():
"""root -> [a, b] all DONE: upstream status should be ''."""
return FakeState([
FakeTask("root", PENDING, ["a", "b"]),
FakeTask("a", DONE, []),
FakeTask("b", DONE, []),
])
# ---------------------------------------------------------------------------
# Tests
# ---------------------------------------------------------------------------
def test_complete_dag_complexity():
"""
Complete DAG at N=30: O(N^2) edges defect does ~8x more pops than fix.
This is the canonical demonstration of the O(edges) vs O(nodes) gap.
"""
n = 30
state = make_complete_dag(n)
_, defect_pops = upstream_status_defect("root", state)
_, fixed_pops = upstream_status_fixed("root", state)
ratio = defect_pops / fixed_pops
assert ratio > 5, (
f"test_complete_dag_complexity: expected ratio > 5x, got {ratio:.1f}x "
f"(defect={defect_pops} pops, fixed={fixed_pops} pops)"
)
print(
f"PASS test_complete_dag_complexity: "
f"defect={defect_pops} pops, fixed={fixed_pops} pops, ratio={ratio:.1f}x"
)
def test_complete_dag_scaling():
"""
Ratio must grow with N, confirming super-linear defect growth.
Ratio at N=50 should exceed ratio at N=20 by a comfortable margin.
"""
def measure(n):
s = make_complete_dag(n)
_, d = upstream_status_defect("root", s)
_, f = upstream_status_fixed("root", s)
return d / f
ratio_20 = measure(20)
ratio_50 = measure(50)
assert ratio_50 > ratio_20 * 1.5, (
f"test_complete_dag_scaling: ratio should grow with N; "
f"ratio@20={ratio_20:.1f}x ratio@50={ratio_50:.1f}x"
)
print(
f"PASS test_complete_dag_scaling: "
f"ratio@N=20={ratio_20:.1f}x → ratio@N=50={ratio_50:.1f}x (growing)"
)
def test_correctness_complete_dag():
"""
Both implementations must return the same upstream status for the complete DAG.
The leaf is FAILED, so root should report UPSTREAM_FAILED.
"""
for n in [5, 15, 30]:
state = make_complete_dag(n)
defect_result, _ = upstream_status_defect("root", state)
fixed_result, _ = upstream_status_fixed("root", state)
assert defect_result == fixed_result, (
f"test_correctness_complete_dag N={n}: "
f"defect='{defect_result}', fixed='{fixed_result}'"
)
assert fixed_result == "UPSTREAM_FAILED", (
f"test_correctness_complete_dag N={n}: expected UPSTREAM_FAILED, got '{fixed_result}'"
)
print("PASS test_correctness_complete_dag: status=UPSTREAM_FAILED for N=5,15,30")
def test_diamond_shared_node():
"""
Simple diamond: N parents all depending on one shared leaf.
Defect pushes shared N times; fix pushes it once.
"""
n = 20
state = make_diamond_state(n)
_, defect_pops = upstream_status_defect("root", state)
_, fixed_pops = upstream_status_fixed("root", state)
# defect pops shared_leaf N times wastefully; fixed pops it once
assert defect_pops > fixed_pops, (
f"test_diamond_shared_node: expected defect > fixed, "
f"got defect={defect_pops}, fixed={fixed_pops}"
)
print(
f"PASS test_diamond_shared_node: "
f"defect={defect_pops} pops, fixed={fixed_pops} pops "
f"(defect enqueues shared {n} times)"
)
def test_correctness_diamond():
"""Both versions must agree on upstream status for diamond graph."""
for n in [5, 20, 50]:
state = make_diamond_state(n)
defect_result, _ = upstream_status_defect("root", state)
fixed_result, _ = upstream_status_fixed("root", state)
assert defect_result == fixed_result, (
f"test_correctness_diamond N={n}: "
f"defect='{defect_result}', fixed='{fixed_result}'"
)
print("PASS test_correctness_diamond: status matches for N=5,20,50")
def test_no_regression_linear_chain():
"""
Linear chain has no shared nodes so both implementations do identical work.
Pop counts should be equal (no wasted pushes possible).
"""
state = make_linear_chain()
_, defect_pops = upstream_status_defect("A", state)
_, fixed_pops = upstream_status_fixed("A", state)
assert defect_pops == fixed_pops, (
f"test_no_regression_linear_chain: expected equal pops, "
f"got defect={defect_pops}, fixed={fixed_pops}"
)
print(
f"PASS test_no_regression_linear_chain: "
f"both={defect_pops} pops (linear chain, no difference)"
)
def test_done_tasks_are_skipped():
"""
Tasks with DONE status are skipped; upstream status of root should be ''.
Both versions must agree and return ''.
"""
state = make_all_done()
defect_result, _ = upstream_status_defect("root", state)
fixed_result, _ = upstream_status_fixed("root", state)
assert defect_result == fixed_result == "", (
f"test_done_tasks_are_skipped: "
f"expected '' got defect='{defect_result}', fixed='{fixed_result}'"
)
print("PASS test_done_tasks_are_skipped: both return '' when all deps DONE")
# ---------------------------------------------------------------------------
# Entry point
# ---------------------------------------------------------------------------
if __name__ == "__main__":
test_complete_dag_complexity()
test_complete_dag_scaling()
test_correctness_complete_dag()
test_diamond_shared_node()
test_correctness_diamond()
test_no_regression_linear_chain()
test_done_tasks_are_skipped()
print("ALL PASS")
sys.exit(0)

View file

@ -0,0 +1,61 @@
# UNDF: UNDF-2026-000000174
diff --git a/src/main/java/org/apache/ibatis/mapping/ResultMapping.java b/src/main/java/org/apache/ibatis/mapping/ResultMapping.java
--- a/src/main/java/org/apache/ibatis/mapping/ResultMapping.java
+++ b/src/main/java/org/apache/ibatis/mapping/ResultMapping.java
@@ -18,8 +18,9 @@
import java.util.ArrayList;
import java.util.Collections;
+import java.util.EnumSet;
import java.util.List;
import java.util.Set;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.type.JdbcType;
import org.apache.ibatis.type.TypeHandler;
@@ -42,7 +43,7 @@ public class ResultMapping {
private Set<String> notNullColumns;
private String columnPrefix;
- private List<ResultFlag> flags;
+ private Set<ResultFlag> flags;
private List<ResultMapping> composites;
private String resultSet;
private String foreignColumn;
@@ -66,7 +67,7 @@ public class ResultMapping {
public Builder(Configuration configuration, String property) {
resultMapping.configuration = configuration;
resultMapping.property = property;
- resultMapping.flags = new ArrayList<>();
+ resultMapping.flags = EnumSet.noneOf(ResultFlag.class);
resultMapping.composites = new ArrayList<>();
resultMapping.lazy = configuration.isLazyLoadingEnabled();
}
@@ -133,7 +134,11 @@ public class ResultMapping {
public Builder flags(List<ResultFlag> flags) {
- resultMapping.flags = flags;
+ // Convert to EnumSet for O(1) contains() — ArrayList.contains() is O(F) per call
+ EnumSet<ResultFlag> enumFlags = EnumSet.noneOf(ResultFlag.class);
+ if (flags != null && !flags.isEmpty()) {
+ enumFlags.addAll(flags);
+ }
+ resultMapping.flags = enumFlags;
return this;
}
@@ -153,7 +158,7 @@ public class ResultMapping {
public ResultMapping build() {
// lock down collections
- resultMapping.flags = Collections.unmodifiableList(resultMapping.flags);
+ resultMapping.flags = Collections.unmodifiableSet(resultMapping.flags);
resultMapping.composites = Collections.unmodifiableList(resultMapping.composites);
validate();
return resultMapping;
@@ -230,7 +235,7 @@ public class ResultMapping {
return columnPrefix;
}
- public List<ResultFlag> getFlags() {
+ public Set<ResultFlag> getFlags() {
return flags;
}

View file

@ -0,0 +1,243 @@
package unit;
import java.util.*;
/**
* MybatisTest CWE-407 benchmark for mybatis-0001 and mybatis-0002
*
* mybatis-0001: ResultMapping.flags List<ResultFlag> contains() in loop
* ResultMap.Builder.build() iterates resultMappings calling
* resultMapping.getFlags().contains(ResultFlag.CONSTRUCTOR) and
* resultMapping.getFlags().contains(ResultFlag.ID) each call is O(F)
* where F = flags per mapping, making the full loop O(N×F).
* Fix: change flags from ArrayList to EnumSet O(1) contains() O(N) total.
*
* mybatis-0002: MapperBuilderAssistant.addResultMap() removeIf scan
* extendedResultMappings.removeIf(rm -> rm.getFlags().contains(ResultFlag.CONSTRUCTOR))
* Same O(F) per mapping; auto-fixed by mybatis-0001's EnumSet change.
*/
public class MybatisTest {
// Sentinel enum matching ResultFlag's structure: exactly 2 values
enum ResultFlag { ID, CONSTRUCTOR }
// ---------------------------------------------------------------------------
// mybatis-0001: flags contains() in ResultMap build() loop
// ---------------------------------------------------------------------------
/**
* Simulates ResultMap.Builder.build() loop with ArrayList<ResultFlag>.
*
* For each of N result mappings, calls flags.contains(CONSTRUCTOR) and
* flags.contains(ID) each is an O(F) linear scan of the flags list.
*
* Total probes = N * (avgScanLengthForCONSTRUCTOR + avgScanLengthForID)
* Worst case (flag not present): N * 2 * F probes.
*
* @param numMappings N number of result mappings
* @param numFlags F flags per mapping (worst case: flag not found full scan)
* @return total ArrayList element comparisons executed
*/
static long slowFlagsContains(int numMappings, int numFlags) {
// Build a flag list that does NOT contain CONSTRUCTOR or ID
// so every contains() call does a full scan of all F elements
List<ResultFlag> flagTemplate = new ArrayList<>(numFlags);
// Fill with alternating ID/CONSTRUCTOR but exclude the search targets
// by using an empty list (no flags) each contains() scans 0 elements
// That's trivial; instead we want a list that is populated but misses the target.
// Use a list where all F slots are filled with a "wrong" value by repeating
// the opposite flag. Since ResultFlag only has 2 values, we'll simulate
// arbitrary flag objects using Integer to represent F distinct flag-like tokens.
//
// Actually: simulate with a List<Integer> of size F, searching for -1 (not present).
// This models the O(F) worst-case scan faithfully.
long probes = 0;
for (int i = 0; i < numMappings; i++) {
// Build flags list for this mapping F elements, target not present
List<Integer> flags = new ArrayList<>(numFlags);
for (int j = 0; j < numFlags; j++) {
flags.add(j); // values 0..F-1, none equal to -1
}
// Simulate getFlags().contains(CONSTRUCTOR) full scan (not found)
int target = -1;
for (int j = 0; j < flags.size(); j++) {
probes++;
if (flags.get(j).equals(target)) break;
}
// Simulate getFlags().contains(ID) second full scan
for (int j = 0; j < flags.size(); j++) {
probes++;
if (flags.get(j).equals(target)) break;
}
}
return probes;
}
/**
* Simulates the fixed version: flags stored as EnumSet (or equivalent Set).
* Set.contains() is O(1) modeled as exactly 1 probe per call.
*
* @param numMappings N
* @param numFlags F (irrelevant for complexity included for symmetry)
* @return total probes: N * 2 (two O(1) lookups per mapping)
*/
static long fastFlagsContains(int numMappings, int numFlags) {
long probes = 0;
for (int i = 0; i < numMappings; i++) {
// Build flags as a HashSet (models EnumSet)
Set<Integer> flags = new HashSet<>(numFlags * 2);
for (int j = 0; j < numFlags; j++) {
flags.add(j);
}
// O(1) contains check count as 1 probe each
probes++; // contains(CONSTRUCTOR)
flags.contains(-1);
probes++; // contains(ID)
flags.contains(-1);
}
return probes;
}
// ---------------------------------------------------------------------------
// mybatis-0002: removeIf(rm -> rm.getFlags().contains(CONSTRUCTOR))
// ---------------------------------------------------------------------------
/**
* Simulates addResultMap() removeIf with ArrayList.contains() O(R×F).
* R = result mappings in extendedResultMappings, F = flags per mapping.
*
* @param numMappings R
* @param numFlags F
* @return total probes across all removeIf predicate evaluations
*/
static long slowRemoveIf(int numMappings, int numFlags) {
long probes = 0;
for (int i = 0; i < numMappings; i++) {
// Simulate flags.contains(CONSTRUCTOR) for each mapping O(F) scan
List<Integer> flags = new ArrayList<>(numFlags);
for (int j = 0; j < numFlags; j++) {
flags.add(j);
}
int target = -1;
for (int j = 0; j < flags.size(); j++) {
probes++;
if (flags.get(j).equals(target)) break;
}
}
return probes;
}
/**
* Simulates fixed removeIf with EnumSet.contains() O(R).
*
* @param numMappings R
* @param numFlags F (irrelevant)
* @return total probes: R * 1
*/
static long fastRemoveIf(int numMappings, int numFlags) {
long probes = 0;
for (int i = 0; i < numMappings; i++) {
Set<Integer> flags = new HashSet<>(numFlags * 2);
for (int j = 0; j < numFlags; j++) {
flags.add(j);
}
probes++; // O(1) Set.contains
flags.contains(-1);
}
return probes;
}
// ---------------------------------------------------------------------------
// Benchmarking helper
// ---------------------------------------------------------------------------
static void bench(String label, long slowOps, long fastOps) {
double ratio = (double) slowOps / Math.max(fastOps, 1);
System.out.printf(" %-66s slow:%,8d ops fast:%,8d ops ratio:%.0fx%n",
label, slowOps, fastOps, ratio);
}
// ---------------------------------------------------------------------------
// main
// ---------------------------------------------------------------------------
public static void main(String[] args) {
System.out.println("MybatisTest — CWE-407: mybatis-0001 + mybatis-0002");
System.out.println();
// --- mybatis-0001 benchmarks ---
System.out.println(" [mybatis-0001: ResultMap.build() flags.contains() ArrayList O(N×F) → EnumSet O(N)]");
int[][] cases0001 = {{100, 10}, {500, 20}, {1000, 50}};
for (int[] c : cases0001) {
int n = c[0], f = c[1];
long slow = slowFlagsContains(n, f);
long fast = fastFlagsContains(n, f);
bench(String.format("N=%d mappings, F=%d flags", n, f), slow, fast);
}
System.out.println();
// --- mybatis-0002 benchmarks ---
System.out.println(" [mybatis-0002: addResultMap() removeIf flags.contains() ArrayList O(R×F) → EnumSet O(R)]");
int[][] cases0002 = {{100, 10}, {500, 20}, {1000, 50}};
for (int[] c : cases0002) {
int r = c[0], f = c[1];
long slow = slowRemoveIf(r, f);
long fast = fastRemoveIf(r, f);
bench(String.format("R=%d mappings, F=%d flags", r, f), slow, fast);
}
System.out.println();
// --- assertions ---
int pass = 0;
int total = 0;
// mybatis-0001: at N=1000, F=50 slow = 1000*2*50 = 100_000; fast = 1000*2 = 2000 ratio 50x
{
total++;
int n = 1000, f = 50;
long slow = slowFlagsContains(n, f);
long fast = fastFlagsContains(n, f);
double ratio = (double) slow / Math.max(fast, 1);
boolean ok = ratio > 10.0;
if (ok) pass++;
System.out.printf(" %s mybatis-0001: ArrayList.contains O(N×F) → EnumSet O(N)"
+ " N=%d F=%d slow=%,d fast=%,d ratio=%.0fx%n",
ok ? "PASS" : "FAIL", n, f, slow, fast, ratio);
if (!ok) {
System.err.println(" FAIL mybatis-0001: expected ratio > 10x, got " + ratio);
}
}
// mybatis-0002: at R=1000, F=50 slow = 50_000; fast = 1000 ratio 50x
{
total++;
int r = 1000, f = 50;
long slow = slowRemoveIf(r, f);
long fast = fastRemoveIf(r, f);
double ratio = (double) slow / Math.max(fast, 1);
boolean ok = ratio > 10.0;
if (ok) pass++;
System.out.printf(" %s mybatis-0002: removeIf ArrayList.contains O(R×F) → EnumSet O(R)"
+ " R=%d F=%d slow=%,d fast=%,d ratio=%.0fx%n",
ok ? "PASS" : "FAIL", r, f, slow, fast, ratio);
if (!ok) {
System.err.println(" FAIL mybatis-0002: expected ratio > 10x, got " + ratio);
}
}
System.out.println();
System.out.printf("%d/%d PASS%n", pass, total);
if (pass < total) {
System.out.println("FAIL");
System.exit(1);
}
System.out.println("ALL PASS");
}
}

View file

@ -1,4 +1,3 @@
# UNDF: UNDF-2026-000000201
--- a/usr.sbin/smtpd/ruleset.c
+++ b/usr.sbin/smtpd/ruleset.c
@@ -18,6 +18,8 @@

View file

@ -0,0 +1,27 @@
# CLEAN — Solana
Scanned 2026-03-30 for CWE-407.
## Scope
- `runtime/src/bank.rs` — transaction processing, account key iteration
- `runtime/src/non_circulating_supply.rs` — stake account stake authority check
- `core/src/repair/repair_service.rs` — slot repair range
- `core/src/banking_stage/`, `core/src/consensus/` — deduplication paths
## Findings
| Location | Pattern | Data Structure | Result |
|----------|---------|----------------|--------|
| `bank.rs:4671` | `debug_keys.contains(key)` per account key | `HashSet<Pubkey>` | CLEAN |
| `bank.rs:4692` | `mentioned_addresses.contains(key)` per account key | `HashSet<Pubkey>` | CLEAN |
| `non_circulating_supply.rs:56` | `withdraw_authority_list.contains(addr)` in stake loop | `&[Pubkey]` slice, fixed N=10 | LOW — constant factor, not scaling |
| `repair_service.rs:1346` | `slots.contains(&slot_index)` in map closure | `Vec<u64>` | TEST CODE ONLY |
| `banking_stage/read_write_account_set.rs` | per-account contains | `HashSet<Pubkey>` | CLEAN |
| `consensus/vote_stake_tracker.rs` | voted.contains | `HashSet<Pubkey>` | CLEAN |
The `withdraw_authority_list` contains exactly 10 hard-coded program addresses (a fixed constant).
The per-stake-account linear scan is O(10) per account — effectively O(1), not a scaling defect.
The `repair_service.rs` instance is in a test helper, not production code.
**Result: No actionable CWE-407 defects.**

View file

@ -0,0 +1,45 @@
# solc-0002 — ContractLevelChecker findDuplicateDefinitions — CLEAN (CWE-407)
## File
`libsolidity/analysis/ContractLevelChecker.cpp` ~line 239
## Pattern reviewed
```cpp
std::set<size_t> reported;
for (size_t i = 0; i < overloads.size() && !reported.count(i); ++i)
```
## CWE-407 verdict: NOT a performance defect
`std::set<size_t>::count` is O(log N), making the loop O(N log N) — well within
acceptable complexity for the use case. N is bounded by the number of overloaded
definitions of a single name in a contract, which in practice is tiny (< 20).
No CWE-407 patch warranted.
## Separate logic concern (not CWE-407)
The `!reported.count(i)` loop guard has a latent correctness defect: if index `i`
was inserted into `reported` as a *duplicate* during an earlier outer iteration
(e.g., iteration i=0 found j=2 is a duplicate and inserted 2), then when the outer
loop reaches i=2 the guard fires and the loop exits early. This causes i=2 to be
silently skipped as a *primary* candidate, potentially missing further duplicates
whose first occurrence is at index 2.
This is a correctness/logic defect, not a CWE-407 algorithmic-complexity defect.
It should be tracked separately if it produces missed duplicate-definition errors
in practice.
## Recommendation
Change the loop guard to allow all `i` to be visited as primary candidates:
```cpp
for (size_t i = 0; i < overloads.size(); ++i)
{
if (reported.count(i)) continue; // already flagged as someone else's dup
...
}
```
This preserves the intent (skip reporting i if it was already reported as a
duplicate of an earlier declaration) while not prematurely terminating the loop.

View file

@ -0,0 +1,61 @@
# UNDF: UNDF-2026-000000288
--- a/libsolidity/analysis/TypeChecker.cpp
+++ b/libsolidity/analysis/TypeChecker.cpp
@@ -3611,9 +3611,17 @@ std::vector<Declaration const*> TypeChecker::cleanOverloadedDeclarations(
Identifier const& _identifier,
std::vector<Declaration const*> const& _candidates
)
{
solAssert(_candidates.size() > 1, "");
+
+ // CWE-407 fix: use a set keyed by canonical parameter-type string to avoid
+ // the O(N²) find_if scan. For each incoming declaration we resolve its
+ // FunctionType once, serialise the parameter+return types into a string key,
+ // and do an O(1) unordered_set probe instead of a linear walk over
+ // uniqueDeclarations. The original code called declaration->functionType()
+ // twice (false then true) inside an inner lambda that was invoked once per
+ // already-accumulated unique entry — O(N²) function-type resolutions in the
+ // worst case where every declaration has a distinct signature.
std::vector<Declaration const*> uniqueDeclarations;
+ std::unordered_set<std::string> seenSignatures;
for (Declaration const* declaration: _candidates)
{
@@ -3631,15 +3639,17 @@ std::vector<Declaration const*> TypeChecker::cleanOverloadedDeclarations(
FunctionTypePointer functionType {declaration->functionType(false)};
if (!functionType)
functionType = declaration->functionType(true);
solAssert(functionType, "Failed to determine the function type of the overloaded.");
for (Type const* parameter: functionType->parameterTypes() + functionType->returnParameterTypes())
if (!parameter)
m_errorReporter.fatalDeclarationError(3893_error, _identifier.location(), "Function type can not be used in this context.");
- if (uniqueDeclarations.end() == find_if(
- uniqueDeclarations.begin(),
- uniqueDeclarations.end(),
- [&](Declaration const* d)
- {
- FunctionType const* newFunctionType = d->functionType(false);
- if (!newFunctionType)
- newFunctionType = d->functionType(true);
- return newFunctionType && functionType->hasEqualParameterTypes(*newFunctionType);
- }
- ))
+ // Build a canonical signature key from parameter and return types.
+ // Two declarations are considered duplicates iff their parameter types
+ // are equal (hasEqualParameterTypes), so we key on the human-readable
+ // parameter-type string which FunctionType already exposes via
+ // toString(false) on each parameter. This gives O(1) duplicate
+ // detection instead of the prior O(N) find_if per candidate.
+ std::string sigKey;
+ for (Type const* p : functionType->parameterTypes())
+ sigKey += p->toString(false) + ",";
+ sigKey += "|";
+ for (Type const* r : functionType->returnParameterTypes())
+ sigKey += r->toString(false) + ",";
+
+ if (seenSignatures.insert(sigKey).second)
uniqueDeclarations.push_back(declaration);
}
return uniqueDeclarations;

View file

@ -0,0 +1,242 @@
package unit;
import java.util.*;
/**
* SolcTest CWE-407 benchmark for solc-0001
*
* solc-0001: TypeChecker::cleanOverloadedDeclarations
* File: libsolidity/analysis/TypeChecker.cpp, ~line 3619-3651
*
* Real code:
* for (Declaration const* declaration: _candidates)
* {
* ...
* if (uniqueDeclarations.end() == find_if(
* uniqueDeclarations.begin(), uniqueDeclarations.end(),
* [&](Declaration const* d) {
* FunctionType const* newFunctionType = d->functionType(false);
* if (!newFunctionType) newFunctionType = d->functionType(true);
* return newFunctionType && functionType->hasEqualParameterTypes(*newFunctionType);
* }
* ))
* uniqueDeclarations.push_back(declaration);
* }
*
* For N declarations each with a unique signature the inner find_if scans
* 0 + 1 + 2 + ... + (N-1) = N*(N-1)/2 entries O(N²) comparisons.
*
* Fix: build a seenSignatures unordered_set keyed by the canonical parameter-type
* string. Each probe/insert is O(1) total O(N).
*
* solc-0002: ContractLevelChecker::findDuplicateDefinitions CLEAN (CWE-407)
* The set<size_t>::count call is O(log N), making the loop O(N log N) fine
* for small N (< 20 overloads per name). A separate logic defect exists
* (loop exits early when i is in reported), but that is not a CWE-407 issue.
* No performance patch warranted.
*/
public class SolcTest {
// -----------------------------------------------------------------------
// solc-0001 simulation
// -----------------------------------------------------------------------
/**
* Simulates the defective cleanOverloadedDeclarations.
*
* Each "declaration" is represented by its signature string.
* uniqueDeclarations accumulates strings already accepted.
* For each new candidate we scan uniqueDeclarations linearly to check
* whether an equal signature already exists mirroring the find_if loop.
*
* @param signatures ordered list of candidate signatures (may contain dupes)
* @return number of comparisons performed (the ops count)
*/
static long slowCleanOverloads(List<String> signatures) {
List<String> uniqueDeclarations = new ArrayList<>();
long ops = 0;
for (String sig : signatures) {
boolean found = false;
// O(|uniqueDeclarations|) linear scan mirrors std::find_if
for (String existing : uniqueDeclarations) {
ops++;
if (existing.equals(sig)) {
found = true;
break;
}
}
if (!found) {
uniqueDeclarations.add(sig);
}
}
return ops;
}
/**
* Simulates the fixed cleanOverloadedDeclarations.
*
* Uses an unordered_set (HashSet) for O(1) duplicate detection.
* Each candidate signature is probed / inserted in O(1).
*
* @param signatures ordered list of candidate signatures
* @return number of ops charged (1 per candidate = O(N))
*/
static long fastCleanOverloads(List<String> signatures) {
Set<String> seenSignatures = new HashSet<>();
List<String> uniqueDeclarations = new ArrayList<>();
long ops = 0;
for (String sig : signatures) {
ops++; // O(1) hash probe + insert
if (seenSignatures.add(sig)) {
uniqueDeclarations.add(sig);
}
}
return ops;
}
// -----------------------------------------------------------------------
// Helpers
// -----------------------------------------------------------------------
/** Build a list of N distinct signatures (all unique — worst case for find_if). */
static List<String> distinctSignatures(int n) {
List<String> sigs = new ArrayList<>(n);
for (int i = 0; i < n; i++) {
// Simulate paramater-type strings like "uint256,address|bool,"
sigs.add("param" + i + "_type:uint256_" + i + "|ret:bool,");
}
return sigs;
}
/** Build a list of N signatures where half are duplicates of the first half. */
static List<String> halfDupSignatures(int n) {
List<String> sigs = new ArrayList<>(n);
for (int i = 0; i < n / 2; i++) sigs.add("sig_" + i);
for (int i = 0; i < n / 2; i++) sigs.add("sig_" + i); // duplicates
return sigs;
}
static void bench(String label, List<String> sigs) {
// warm-up
slowCleanOverloads(sigs);
fastCleanOverloads(sigs);
long t0 = System.nanoTime();
long sOps = slowCleanOverloads(sigs);
long slowNs = System.nanoTime() - t0;
long t1 = System.nanoTime();
long fOps = fastCleanOverloads(sigs);
long fastNs = System.nanoTime() - t1;
double ratio = (double) sOps / Math.max(fOps, 1);
System.out.printf(" %-60s slow:%6dns (%,6d ops) fast:%6dns (%,6d ops) ratio:%.1fx%n",
label, slowNs, sOps, fastNs, fOps, ratio);
}
// -----------------------------------------------------------------------
// main
// -----------------------------------------------------------------------
public static void main(String[] args) {
System.out.println("SolcTest — solc-0001: TypeChecker::cleanOverloadedDeclarations O(N²) → O(N)");
System.out.println();
System.out.println(" Benchmark: all-distinct signatures (worst case for find_if)");
int[] sizes = {50, 100, 200, 500};
for (int n : sizes) {
bench(String.format("N=%d distinct overload candidates", n), distinctSignatures(n));
}
System.out.println();
System.out.println(" Benchmark: half-duplicate signatures");
for (int n : sizes) {
bench(String.format("N=%d half-dup overload candidates", n), halfDupSignatures(n));
}
System.out.println();
// ---- Assertions ----
int pass = 0;
int total = 0;
// Test 1: N=100 distinct slow must be ~N*(N-1)/2 comparisons, fast = N
{
total++;
int n = 100;
List<String> sigs = distinctSignatures(n);
long sOps = slowCleanOverloads(sigs);
long fOps = fastCleanOverloads(sigs);
long expectedSlow = (long) n * (n - 1) / 2; // 4950
assert sOps == expectedSlow :
"solc-0001 slow ops mismatch: expected " + expectedSlow + " got " + sOps;
assert fOps == n :
"solc-0001 fast ops mismatch: expected " + n + " got " + fOps;
double ratio = (double) sOps / fOps;
assert ratio > 10.0 :
"solc-0001 expected >10x ratio at N=100; got " + ratio;
System.out.printf(" PASS [1/%d] N=100 distinct: slow=%d ops, fast=%d ops, ratio=%.1fx (expected >10x)%n",
total, sOps, fOps, ratio);
pass++;
}
// Test 2: N=200 distinct ratio should be ~100x
{
total++;
int n = 200;
List<String> sigs = distinctSignatures(n);
long sOps = slowCleanOverloads(sigs);
long fOps = fastCleanOverloads(sigs);
double ratio = (double) sOps / fOps;
assert ratio > 50.0 :
"solc-0001 expected >50x ratio at N=200; got " + ratio;
System.out.printf(" PASS [2/%d] N=200 distinct: slow=%d ops, fast=%d ops, ratio=%.1fx (expected >50x)%n",
total, sOps, fOps, ratio);
pass++;
}
// Test 3: half-dup N=100 slow still O(N²/4), fast O(N)
{
total++;
int n = 100;
List<String> sigs = halfDupSignatures(n);
long sOps = slowCleanOverloads(sigs);
long fOps = fastCleanOverloads(sigs);
double ratio = (double) sOps / fOps;
assert ratio > 10.0 :
"solc-0001 half-dup expected >10x ratio at N=100; got " + ratio;
System.out.printf(" PASS [3/%d] N=100 half-dup: slow=%d ops, fast=%d ops, ratio=%.1fx (expected >10x)%n",
total, sOps, fOps, ratio);
pass++;
}
// Test 4: output correctness both methods must agree on unique-count
{
total++;
int n = 100;
List<String> sigs = halfDupSignatures(n);
// count uniques via slow
List<String> slowUniques = new ArrayList<>();
for (String sig : sigs) {
if (!slowUniques.contains(sig)) slowUniques.add(sig);
}
// count uniques via fast
Set<String> fastUniques = new LinkedHashSet<>(sigs);
assert slowUniques.size() == fastUniques.size() :
"solc-0001 output mismatch: slow=" + slowUniques.size() + " fast=" + fastUniques.size();
assert slowUniques.size() == n / 2 :
"solc-0001 expected " + (n / 2) + " unique sigs; got " + slowUniques.size();
System.out.printf(" PASS [4/%d] Output correctness: both methods yield %d unique sigs from %d candidates%n",
total, slowUniques.size(), n);
pass++;
}
System.out.println();
System.out.printf("%d/%d PASS%n", pass, total);
System.out.printf("ALL PASS%n");
System.out.println();
System.out.printf("solc-0001: TypeChecker::cleanOverloadedDeclarations — vector find_if O(N²) → unordered_set O(N)%n");
System.out.printf("Hotpath: every identifier resolution with overloaded declarations in Solidity source%n");
System.out.printf("solc-0002: ContractLevelChecker::findDuplicateDefinitions — CLEAN (CWE-407); set::count O(log N) acceptable for small N%n");
}
}

View file

@ -0,0 +1,25 @@
# UNDF: UNDF-2026-000000343
--- a/zookeeper-server/src/main/java/org/apache/zookeeper/server/PrepRequestProcessor.java
+++ b/zookeeper-server/src/main/java/org/apache/zookeeper/server/PrepRequestProcessor.java
@@ -24,6 +24,7 @@
import java.util.Collections;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
@@ -948,11 +949,12 @@
- // This would be done better with a Set but ACL hashcode/equals do not
- // allow for null values
- final ArrayList<ACL> retval = new ArrayList<>(acls.size());
+ // CWE-407 fix: parallel HashSet for O(1) membership test instead of O(N) ArrayList.contains
+ final List<ACL> retval = new ArrayList<>(acls.size());
+ final Set<ACL> seen = new HashSet<>(acls.size());
for (final ACL acl : acls) {
- if (!retval.contains(acl)) {
- retval.add(acl);
+ if (seen.add(acl)) {
+ retval.add(acl);
}
}
return retval;

View file

@ -0,0 +1,20 @@
# UNDF: UNDF-2026-000000724
--- a/zookeeper-server/src/main/java/org/apache/zookeeper/server/AuthenticationHelper.java
+++ b/zookeeper-server/src/main/java/org/apache/zookeeper/server/AuthenticationHelper.java
@@ -20,7 +20,9 @@
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.HashSet;
import java.util.List;
+import java.util.Set;
import java.util.stream.Collectors;
import org.apache.zookeeper.KeeperException;
import org.apache.zookeeper.data.Id;
@@ -44,7 +46,7 @@
private boolean enforceAuthEnabled;
- private List<String> enforceAuthSchemes = new ArrayList<>();
+ private Set<String> enforceAuthSchemes = new HashSet<>();
private boolean saslAuthRequired;
public AuthenticationHelper() {

View file

@ -0,0 +1,213 @@
package unit;
import java.util.*;
/**
* ZookeeperTest CWE-407 benchmark for zookeeper-0001 and zookeeper-0002
*
* zookeeper-0001: PrepRequestProcessor.removeDuplicates()
* ArrayList.contains() scan on growing retval O(N²) total for N ACL entries
* Fix: parallel HashSet<ACL> seen O(N) total, O(1) per check
*
* zookeeper-0002: AuthenticationHelper.isCnxnAuthenticated()
* ArrayList<String>.contains() for enforceAuthSchemes O(M) per auth id, O(I×M) total
* Fix: HashSet<String> enforceAuthSchemes O(1) per auth id, O(I) total
*/
public class ZookeeperTest {
// ---------------------------------------------------------------------------
// zookeeper-0001: ACL deduplication
// ---------------------------------------------------------------------------
/**
* Simulates removeDuplicates() with ArrayList.contains() O(N²) contain calls.
* Returns the total number of contains() probes executed.
*/
static long slowRemoveDuplicates(int totalAcls, int uniqueAcls) {
// Build input: cycle through uniqueAcls distinct values
List<Integer> input = new ArrayList<>(totalAcls);
for (int i = 0; i < totalAcls; i++) {
input.add(i % uniqueAcls);
}
long probes = 0;
List<Integer> retval = new ArrayList<>(totalAcls);
for (Integer acl : input) {
// Simulate ArrayList.contains() scan every element already in retval
boolean found = false;
for (int j = 0; j < retval.size(); j++) {
probes++;
if (retval.get(j).equals(acl)) {
found = true;
break;
}
}
if (!found) {
retval.add(acl);
}
}
return probes;
}
/**
* Simulates fixed removeDuplicates() with HashSet.add() O(N) total.
* Returns the number of set probes (one per element).
*/
static long fastRemoveDuplicates(int totalAcls, int uniqueAcls) {
List<Integer> input = new ArrayList<>(totalAcls);
for (int i = 0; i < totalAcls; i++) {
input.add(i % uniqueAcls);
}
long probes = 0;
List<Integer> retval = new ArrayList<>(totalAcls);
Set<Integer> seen = new HashSet<>(totalAcls);
for (Integer acl : input) {
probes++; // one O(1) HashSet.add probe per element
if (seen.add(acl)) {
retval.add(acl);
}
}
return probes;
}
// ---------------------------------------------------------------------------
// zookeeper-0002: auth scheme membership check
// ---------------------------------------------------------------------------
/**
* Simulates isCnxnAuthenticated() with ArrayList.contains() O(I×M).
* I = number of auth ids on the connection, M = number of enforced schemes.
* Returns total probes across all scheme lookups.
*/
static long slowIsCnxnAuthenticated(int authIds, int schemes) {
// Build enforceAuthSchemes as ArrayList
List<String> enforceAuthSchemes = new ArrayList<>();
for (int i = 0; i < schemes; i++) {
enforceAuthSchemes.add("scheme_" + i);
}
// Build cnxn authInfo none of the ids match (worst case: scan all schemes)
List<String> authInfo = new ArrayList<>();
for (int i = 0; i < authIds; i++) {
authInfo.add("unknown_" + i);
}
long probes = 0;
for (String scheme : authInfo) {
// Simulate ArrayList.contains() full scan of enforceAuthSchemes list
for (int j = 0; j < enforceAuthSchemes.size(); j++) {
probes++;
if (enforceAuthSchemes.get(j).equals(scheme)) {
break; // found stop scanning
}
}
}
return probes;
}
/**
* Simulates fixed isCnxnAuthenticated() with HashSet.contains() O(I).
* Returns total probes (one per auth id).
*/
static long fastIsCnxnAuthenticated(int authIds, int schemes) {
Set<String> enforceAuthSchemes = new HashSet<>();
for (int i = 0; i < schemes; i++) {
enforceAuthSchemes.add("scheme_" + i);
}
List<String> authInfo = new ArrayList<>();
for (int i = 0; i < authIds; i++) {
authInfo.add("unknown_" + i);
}
long probes = 0;
for (String scheme : authInfo) {
probes++; // O(1) HashSet.contains per auth id
if (enforceAuthSchemes.contains(scheme)) {
return probes; // authenticated
}
}
return probes;
}
// ---------------------------------------------------------------------------
// Benchmarking helper
// ---------------------------------------------------------------------------
static void bench(String label, long slowOps, long fastOps) {
double ratio = (double) slowOps / Math.max(fastOps, 1);
System.out.printf(" %-62s slow:%,8d ops fast:%,8d ops ratio:%.0fx%n",
label, slowOps, fastOps, ratio);
}
// ---------------------------------------------------------------------------
// main
// ---------------------------------------------------------------------------
public static void main(String[] args) {
System.out.println("ZookeeperTest — CWE-407: zookeeper-0001 + zookeeper-0002");
System.out.println();
// --- zookeeper-0001 benchmarks ---
System.out.println(" [zookeeper-0001: PrepRequestProcessor.removeDuplicates() ACL dedup]");
int[][] aclCases = {{200, 50}, {500, 100}, {1000, 200}};
for (int[] c : aclCases) {
int total = c[0], unique = c[1];
long slow = slowRemoveDuplicates(total, unique);
long fast = fastRemoveDuplicates(total, unique);
bench(String.format("N=%d total ACLs, U=%d unique", total, unique), slow, fast);
}
System.out.println();
// --- zookeeper-0002 benchmarks ---
System.out.println(" [zookeeper-0002: AuthenticationHelper.isCnxnAuthenticated() scheme lookup]");
int[][] authCases = {{10, 50}, {50, 200}, {100, 500}};
for (int[] c : authCases) {
int ids = c[0], schemes = c[1];
long slow = slowIsCnxnAuthenticated(ids, schemes);
long fast = fastIsCnxnAuthenticated(ids, schemes);
bench(String.format("I=%d auth-ids, M=%d enforced-schemes", ids, schemes), slow, fast);
}
System.out.println();
// --- assertions ---
int pass = 0;
// zookeeper-0001: O(N²) vs O(N) at N=1000/U=200, slow >> fast
{
long slow = slowRemoveDuplicates(1000, 200);
long fast = fastRemoveDuplicates(1000, 200);
// slow is O(N×U) worst-case; fast is exactly N probes
// expect ratio 10x
assert slow > fast * 10
: "zookeeper-0001 expected >10x ratio; slow=" + slow + " fast=" + fast;
pass++;
System.out.printf(" PASS zookeeper-0001: ArrayList.contains dedup O(N²) → HashSet.add O(N)"
+ " ratio=%.0fx%n", (double) slow / fast);
}
// zookeeper-0002: O(I×M) vs O(I) at I=100/M=500, slow >> fast
{
long slow = slowIsCnxnAuthenticated(100, 500);
long fast = fastIsCnxnAuthenticated(100, 500);
// slow = 100×500 = 50000; fast = 100
// expect ratio 50x
assert slow > fast * 50
: "zookeeper-0002 expected >50x ratio; slow=" + slow + " fast=" + fast;
pass++;
System.out.printf(" PASS zookeeper-0002: ArrayList.contains auth-scheme O(I×M) → HashSet O(I)"
+ " ratio=%.0fx%n", (double) slow / fast);
}
System.out.println();
System.out.printf("%d/2 PASS%n", pass);
if (pass < 2) {
System.out.println("FAIL");
System.exit(1);
}
System.out.println("ALL PASS");
}
}