wave15: sm-0003/0004 + threejs-0006 + varnish-0002 + mongodb-0008 — 533/240
This commit is contained in:
parent
7146714143
commit
31850d5ef6
13 changed files with 1893 additions and 9 deletions
209
defects/varnish/unit/VarnishBanReloadAlgorithm.java
Normal file
209
defects/varnish/unit/VarnishBanReloadAlgorithm.java
Normal file
|
|
@ -0,0 +1,209 @@
|
|||
package unit;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* varnish-0002: BAN_Reload O(B²) dedup scan.
|
||||
*
|
||||
* During Varnish restart / ban persistence reload, BAN_Reload() calls ban_reload()
|
||||
* once per persisted ban. ban_reload() itself does TWO linear walks over the
|
||||
* existing ban_head list calling ban_equal() (memcmp) on each entry:
|
||||
*
|
||||
* 1. VTAILQ_FOREACH_FROM to detect duplicates at the same timestamp bracket.
|
||||
* 2. A second for-loop that walks ALL older bans to mark them completed.
|
||||
*
|
||||
* Combined: O(B²) comparisons. The code even has an explicit XXX comment:
|
||||
* "This can be optimized by traversing the live ban list together with
|
||||
* the reload list (combining the loops in BAN_Reload and ban_reload)."
|
||||
*
|
||||
* SLOW path: simulates the defect — linear ban_equal scan per loaded ban.
|
||||
* FAST path: simulates the fix — hash pre-filter so only hash-matching bans
|
||||
* need full comparison (typically 0 collisions = 0 full compares).
|
||||
*
|
||||
* Correctness: both paths must produce the same duplicate detection result.
|
||||
* Performance: slow ops >> fast ops, ratio >= 5x at B=500.
|
||||
*/
|
||||
public class VarnishBanReloadAlgorithm {
|
||||
|
||||
// ---- simulated ban spec -------------------------------------------------
|
||||
static class BanSpec {
|
||||
final int id; // unique id standing in for full ban content
|
||||
final long hash; // XXH64-equivalent: id*2654435761L
|
||||
boolean completed;
|
||||
|
||||
BanSpec(int id) {
|
||||
this.id = id;
|
||||
this.hash = id * 2654435761L; // Knuth multiplicative hash
|
||||
this.completed = false;
|
||||
}
|
||||
|
||||
/** ban_equal equivalent: O(1) here, models memcmp on full spec bytes */
|
||||
boolean equalSpec(BanSpec other) {
|
||||
return this.id == other.id;
|
||||
}
|
||||
}
|
||||
|
||||
// ---- Result container ---------------------------------------------------
|
||||
static class Result {
|
||||
long ops; // comparison operations performed
|
||||
int duplicatesFound;
|
||||
Result(long ops, int dups) { this.ops = ops; this.duplicatesFound = dups; }
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// SLOW: ban_reload defect — O(B) linear scan per loaded ban
|
||||
// Simulates the "hunt down older duplicates" for-loop in ban_reload()
|
||||
// -----------------------------------------------------------------------
|
||||
static Result slow(int B) {
|
||||
List<BanSpec> banHead = new ArrayList<>(B);
|
||||
long ops = 0;
|
||||
int dups = 0;
|
||||
|
||||
// Simulate BAN_Reload: load B bans, each doing a full linear dedup scan
|
||||
for (int i = 0; i < B; i++) {
|
||||
BanSpec incoming = new BanSpec(i);
|
||||
|
||||
// "Hunt down older duplicates" — scan all existing bans for ban_equal
|
||||
for (BanSpec existing : banHead) {
|
||||
ops++; // models one ban_equal call
|
||||
if (existing.equalSpec(incoming)) {
|
||||
existing.completed = true; // mark_completed
|
||||
dups++;
|
||||
}
|
||||
}
|
||||
|
||||
banHead.add(incoming);
|
||||
}
|
||||
|
||||
return new Result(ops, dups);
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// FAST: hash pre-filter fix — O(1) hash check before ban_equal
|
||||
// Simulates CWE-407 fix: spec_hash field added to struct ban
|
||||
// Each incoming ban gets hash checked; full memcmp only on hash collision
|
||||
// -----------------------------------------------------------------------
|
||||
static Result fast(int B) {
|
||||
// Map from spec_hash -> list of bans with that hash (collision chain)
|
||||
Map<Long, List<BanSpec>> hashIndex = new HashMap<>(B * 2);
|
||||
long ops = 0;
|
||||
int dups = 0;
|
||||
|
||||
for (int i = 0; i < B; i++) {
|
||||
BanSpec incoming = new BanSpec(i);
|
||||
|
||||
// O(1) hash lookup — finds only candidates with matching hash
|
||||
List<BanSpec> candidates = hashIndex.get(incoming.hash);
|
||||
if (candidates != null) {
|
||||
for (BanSpec candidate : candidates) {
|
||||
ops++; // models one ban_equal call (after hash match)
|
||||
if (candidate.equalSpec(incoming)) {
|
||||
candidate.completed = true;
|
||||
dups++;
|
||||
}
|
||||
}
|
||||
}
|
||||
// CWE-407 fix: ops for the hash index lookup itself counted as 1
|
||||
ops++; // hash lookup O(1)
|
||||
|
||||
// Insert into hash index
|
||||
hashIndex.computeIfAbsent(incoming.hash, k -> new ArrayList<>()).add(incoming);
|
||||
}
|
||||
|
||||
return new Result(ops, dups);
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
public static void main(String[] args) {
|
||||
int[] sizes = {100, 500, 1000};
|
||||
int failures = 0;
|
||||
int tests = 0;
|
||||
|
||||
for (int B : sizes) {
|
||||
Result s = slow(B);
|
||||
Result f = fast(B);
|
||||
|
||||
// Correctness: both paths should find same number of duplicates
|
||||
// (With unique IDs: 0 duplicates in both cases)
|
||||
if (s.duplicatesFound != f.duplicatesFound) {
|
||||
System.out.printf("FAIL [B=%d] correctness: slow_dups=%d fast_dups=%d%n",
|
||||
B, s.duplicatesFound, f.duplicatesFound);
|
||||
failures++;
|
||||
}
|
||||
|
||||
long ratio = s.ops / Math.max(f.ops, 1);
|
||||
// At B=500: slow does ~B²/2 = 124,750 ops, fast does ~B = 500 ops
|
||||
// Expect ratio >= 5x
|
||||
boolean pass = s.ops >= f.ops * 5 && s.duplicatesFound == f.duplicatesFound;
|
||||
tests++;
|
||||
if (!pass) failures++;
|
||||
|
||||
System.out.printf("varnish-0002 B=%-6d slow=%-10d fast=%-8d ratio=%4dx %s%n",
|
||||
B, s.ops, f.ops, ratio, pass ? "PASS" : "FAIL");
|
||||
}
|
||||
|
||||
// Additional test: correctness with actual duplicates
|
||||
// Simulate reloading same ban twice (duplicate scenario)
|
||||
{
|
||||
int B = 200;
|
||||
// Create 200 bans, but first 10 are duplicates of last 10
|
||||
List<BanSpec> banHead = new ArrayList<>();
|
||||
Map<Long, List<BanSpec>> hashIndex = new HashMap<>();
|
||||
long slowOps = 0, fastOps = 0;
|
||||
int slowDups = 0, fastDups = 0;
|
||||
|
||||
// Inject 190 unique bans first
|
||||
for (int i = 0; i < 190; i++) {
|
||||
BanSpec b = new BanSpec(i);
|
||||
// slow: just add
|
||||
banHead.add(b);
|
||||
// fast: index
|
||||
hashIndex.computeIfAbsent(b.hash, k -> new ArrayList<>()).add(b);
|
||||
}
|
||||
|
||||
// Now reload 10 bans that duplicate IDs 0..9
|
||||
for (int i = 0; i < 10; i++) {
|
||||
BanSpec incoming = new BanSpec(i); // duplicate of existing ban i
|
||||
|
||||
// SLOW: linear scan all 190+i bans
|
||||
for (BanSpec existing : banHead) {
|
||||
slowOps++;
|
||||
if (existing.equalSpec(incoming)) {
|
||||
existing.completed = true;
|
||||
slowDups++;
|
||||
}
|
||||
}
|
||||
banHead.add(incoming);
|
||||
|
||||
// FAST: hash lookup
|
||||
List<BanSpec> candidates = hashIndex.get(incoming.hash);
|
||||
if (candidates != null) {
|
||||
for (BanSpec c : candidates) {
|
||||
fastOps++;
|
||||
if (c.equalSpec(incoming)) {
|
||||
c.completed = true;
|
||||
fastDups++;
|
||||
}
|
||||
}
|
||||
}
|
||||
fastOps++;
|
||||
hashIndex.computeIfAbsent(incoming.hash, k -> new ArrayList<>()).add(incoming);
|
||||
}
|
||||
|
||||
boolean correctness = (slowDups == fastDups && slowDups == 10);
|
||||
boolean perf = slowOps > fastOps * 5;
|
||||
boolean pass = correctness && perf;
|
||||
tests++;
|
||||
if (!pass) failures++;
|
||||
System.out.printf("varnish-0002 dedup_correctness slow_dups=%d fast_dups=%d " +
|
||||
"slow_ops=%d fast_ops=%d %s%n",
|
||||
slowDups, fastDups, slowOps, fastOps, pass ? "PASS" : "FAIL");
|
||||
}
|
||||
|
||||
System.out.printf("%n%d/%d PASS%n", tests - failures, tests);
|
||||
if (failures > 0) System.exit(1);
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue