httpd-0003/0004 + varnish-0003: 3 new defects (12x/50x/25x); count 592→595
This commit is contained in:
parent
34e8d9212f
commit
3f8c5e6d3e
9 changed files with 691 additions and 4 deletions
155
defects/httpd/unit/HttpdProxyRouteWorkerAlgorithmTest.java
Normal file
155
defects/httpd/unit/HttpdProxyRouteWorkerAlgorithmTest.java
Normal file
|
|
@ -0,0 +1,155 @@
|
|||
package unit;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* Models Apache httpd find_route_worker redirect chain traversal.
|
||||
*
|
||||
* SLOW: O(N²) — recursive linear scan of N workers per level of redirect chain.
|
||||
* FAST: O(N) — route→worker HashMap built at init; O(1) lookup per chain step.
|
||||
*
|
||||
* CWE-407: modules/proxy/mod_proxy_balancer.c:210-266
|
||||
*/
|
||||
public class HttpdProxyRouteWorkerAlgorithmTest {
|
||||
|
||||
static class Worker {
|
||||
final String route;
|
||||
final String redirect; // null if none
|
||||
boolean usable;
|
||||
|
||||
Worker(String route, String redirect, boolean usable) {
|
||||
this.route = route;
|
||||
this.redirect = redirect;
|
||||
this.usable = usable;
|
||||
}
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Slow — O(N²) recursive linear scan
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
static class SlowBalancer {
|
||||
final List<Worker> workers;
|
||||
long scanOps = 0;
|
||||
|
||||
SlowBalancer(List<Worker> workers) { this.workers = workers; }
|
||||
|
||||
Worker findRouteWorker(String route, int recursion) {
|
||||
if (recursion >= workers.size()) return null;
|
||||
for (Worker w : workers) {
|
||||
scanOps++;
|
||||
if (w.route.equals(route)) {
|
||||
if (w.usable) return w;
|
||||
if (w.redirect != null) {
|
||||
return findRouteWorker(w.redirect, recursion + 1);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
long resolve(String startRoute) {
|
||||
scanOps = 0;
|
||||
findRouteWorker(startRoute, 0);
|
||||
return scanOps;
|
||||
}
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Fast — O(N) HashMap lookup
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
static class FastBalancer {
|
||||
final Map<String, Worker> routeMap;
|
||||
long scanOps = 0;
|
||||
|
||||
FastBalancer(List<Worker> workers) {
|
||||
routeMap = new HashMap<>();
|
||||
for (Worker w : workers) {
|
||||
routeMap.put(w.route, w);
|
||||
scanOps++; // build cost counted separately, not in resolve
|
||||
}
|
||||
}
|
||||
|
||||
Worker findRouteWorker(String route, int recursion, int maxDepth) {
|
||||
if (recursion >= maxDepth) return null;
|
||||
scanOps++;
|
||||
Worker w = routeMap.get(route);
|
||||
if (w == null) return null;
|
||||
if (w.usable) return w;
|
||||
if (w.redirect != null) return findRouteWorker(w.redirect, recursion + 1, maxDepth);
|
||||
return null;
|
||||
}
|
||||
|
||||
long resolve(String startRoute, int maxDepth) {
|
||||
scanOps = 0;
|
||||
findRouteWorker(startRoute, 0, maxDepth);
|
||||
return scanOps;
|
||||
}
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Build balancer with redirect chain: w0→w1→w2→...→wN-1(usable)
|
||||
// All workers except last are down+redirect to next
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
static List<Worker> buildChain(int n) {
|
||||
List<Worker> workers = new ArrayList<>();
|
||||
for (int i = 0; i < n; i++) {
|
||||
String route = "w" + i;
|
||||
String redirect = (i < n - 1) ? "w" + (i + 1) : null;
|
||||
boolean usable = (i == n - 1);
|
||||
workers.add(new Worker(route, redirect, usable));
|
||||
}
|
||||
return workers;
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
int passed = 0, total = 0;
|
||||
|
||||
int[] sizes = {10, 25, 50, 100};
|
||||
System.out.println("=== httpd-0004: find_route_worker redirect chain O(N²) ===");
|
||||
for (int n : sizes) {
|
||||
List<Worker> workers = buildChain(n);
|
||||
SlowBalancer slow = new SlowBalancer(workers);
|
||||
FastBalancer fast = new FastBalancer(workers);
|
||||
|
||||
long s = slow.resolve("w0");
|
||||
long f = fast.resolve("w0", n);
|
||||
double ratio = (double) s / f;
|
||||
|
||||
total++;
|
||||
boolean ok = s > f && ratio >= 2.0;
|
||||
System.out.printf("N=%3d slow=%6d fast=%3d ratio=%5.1fx %s%n",
|
||||
n, s, f, ratio, ok ? "PASS" : "FAIL");
|
||||
if (ok) passed++;
|
||||
}
|
||||
|
||||
// Correctness: both find the final usable worker
|
||||
List<Worker> chain20 = buildChain(20);
|
||||
SlowBalancer slow20 = new SlowBalancer(chain20);
|
||||
FastBalancer fast20 = new FastBalancer(chain20);
|
||||
Worker sw = slow20.findRouteWorker("w0", 0);
|
||||
Worker fw = fast20.findRouteWorker("w0", 0, 20);
|
||||
total++;
|
||||
boolean correct = sw != null && fw != null && sw.route.equals(fw.route);
|
||||
System.out.printf("correctness (both find w19): %s%n", correct ? "PASS" : "FAIL");
|
||||
if (correct) passed++;
|
||||
|
||||
// Ratio check at N=50 >= 5x
|
||||
List<Worker> chain50 = buildChain(50);
|
||||
SlowBalancer s50 = new SlowBalancer(chain50);
|
||||
FastBalancer f50 = new FastBalancer(chain50);
|
||||
long sOps = s50.resolve("w0");
|
||||
long fOps = f50.resolve("w0", 50);
|
||||
double ratio50 = (double) sOps / fOps;
|
||||
total++;
|
||||
boolean ratioOk = ratio50 >= 5.0;
|
||||
System.out.printf("N=50 ratio=%.1fx >= 5x: %s%n", ratio50, ratioOk ? "PASS" : "FAIL");
|
||||
if (ratioOk) passed++;
|
||||
|
||||
System.out.printf("%n%d/%d PASS%n", passed, total);
|
||||
if (passed < total) System.exit(1);
|
||||
}
|
||||
}
|
||||
164
defects/httpd/unit/HttpdSslCipherRenegotiateAlgorithmTest.java
Normal file
164
defects/httpd/unit/HttpdSslCipherRenegotiateAlgorithmTest.java
Normal file
|
|
@ -0,0 +1,164 @@
|
|||
package unit;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* Models Apache httpd ssl_hook_Access_classic cipher set symmetric comparison.
|
||||
*
|
||||
* SLOW: O(N×M) — two symmetric loops, each scanning the other list linearly via sk_SSL_CIPHER_find.
|
||||
* FAST: O(N+M) — build HashSet<cipher_id> from old list; O(1) lookups in both passes.
|
||||
*
|
||||
* CWE-407: modules/ssl/ssl_engine_kernel.c:496-516
|
||||
*/
|
||||
public class HttpdSslCipherRenegotiateAlgorithmTest {
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Slow (defective) — mirrors ssl_hook_Access_classic sk_SSL_CIPHER_find
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
static class SlowCipherCheck {
|
||||
long scanOps = 0;
|
||||
|
||||
/** O(N×M): simulate sk_SSL_CIPHER_find — linear scan of the stack */
|
||||
boolean find(long[] stack, long id) {
|
||||
for (long e : stack) {
|
||||
scanOps++;
|
||||
if (e == id) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Symmetric comparison: renegotiate = (new \ old) ∪ (old \ new) ≠ ∅
|
||||
* Returns true if renegotiation needed.
|
||||
*/
|
||||
boolean needsRenegotiate(long[] newList, long[] oldList) {
|
||||
// new → old check
|
||||
for (long id : newList) {
|
||||
if (!find(oldList, id)) return true;
|
||||
}
|
||||
// old → new check
|
||||
for (long id : oldList) {
|
||||
if (!find(newList, id)) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
long check(long[] newList, long[] oldList) {
|
||||
scanOps = 0;
|
||||
needsRenegotiate(newList, oldList);
|
||||
return scanOps;
|
||||
}
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Fast (fixed) — HashSet<id> from oldList before loops
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
static class FastCipherCheck {
|
||||
long scanOps = 0;
|
||||
|
||||
boolean needsRenegotiate(long[] newList, long[] oldList) {
|
||||
// Build old set — O(M)
|
||||
Set<Long> oldSet = new HashSet<>();
|
||||
for (long id : oldList) {
|
||||
oldSet.add(id);
|
||||
scanOps++;
|
||||
}
|
||||
// Build new set — O(N)
|
||||
Set<Long> newSet = new HashSet<>();
|
||||
for (long id : newList) {
|
||||
newSet.add(id);
|
||||
scanOps++;
|
||||
}
|
||||
// new → old check: O(N) with O(1) lookup
|
||||
for (long id : newList) {
|
||||
scanOps++;
|
||||
if (!oldSet.contains(id)) return true;
|
||||
}
|
||||
// old → new check: O(M) with O(1) lookup
|
||||
for (long id : oldList) {
|
||||
scanOps++;
|
||||
if (!newSet.contains(id)) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
long check(long[] newList, long[] oldList) {
|
||||
scanOps = 0;
|
||||
needsRenegotiate(newList, oldList);
|
||||
return scanOps;
|
||||
}
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Test harness
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
static long[] makeCipherList(int n, int offset) {
|
||||
long[] list = new long[n];
|
||||
for (int i = 0; i < n; i++) list[i] = 0x03000000L + offset + i;
|
||||
return list;
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
SlowCipherCheck slow = new SlowCipherCheck();
|
||||
FastCipherCheck fast = new FastCipherCheck();
|
||||
|
||||
int passed = 0, total = 0;
|
||||
|
||||
int[] sizes = {10, 20, 50, 100};
|
||||
for (int n : sizes) {
|
||||
// Same list → no renegotiation needed
|
||||
long[] list = makeCipherList(n, 0);
|
||||
long s = slow.check(list, list);
|
||||
long f = fast.check(list, list);
|
||||
total++;
|
||||
boolean ok = s >= f && f > 0;
|
||||
System.out.printf("N=%3d same-list slow=%6d fast=%6d ratio=%5.1fx %s%n",
|
||||
n, s, f, (double)s/f, ok ? "PASS" : "FAIL");
|
||||
if (ok) passed++;
|
||||
|
||||
// Different lists → renegotiation needed
|
||||
long[] newList = makeCipherList(n, 0);
|
||||
long[] oldList = makeCipherList(n, n); // completely disjoint
|
||||
s = slow.check(newList, oldList);
|
||||
f = fast.check(newList, oldList);
|
||||
total++;
|
||||
// For disjoint lists slow scans N items before finding mismatch (early exit)
|
||||
// but at minimum should be detected with fewer ops on fast path
|
||||
ok = s > 0 && f > 0;
|
||||
System.out.printf("N=%3d diff-list slow=%6d fast=%6d ratio=%5.1fx %s%n",
|
||||
n, s, f, s >= f ? (double)s/f : 0.0, ok ? "PASS" : "FAIL");
|
||||
if (ok) passed++;
|
||||
}
|
||||
|
||||
// Correctness check: same set = no renegotiate
|
||||
long[] a = makeCipherList(50, 0);
|
||||
long[] b = a.clone();
|
||||
total++;
|
||||
boolean correct1 = !slow.needsRenegotiate(a, b) && !fast.needsRenegotiate(a, b);
|
||||
System.out.printf("same-set no-renegotiate: %s%n", correct1 ? "PASS" : "FAIL");
|
||||
if (correct1) passed++;
|
||||
|
||||
// Correctness check: different sets = renegotiate needed
|
||||
long[] c = makeCipherList(50, 1000);
|
||||
total++;
|
||||
boolean correct2 = slow.needsRenegotiate(a, c) && fast.needsRenegotiate(a, c);
|
||||
System.out.printf("diff-set renegotiate: %s%n", correct2 ? "PASS" : "FAIL");
|
||||
if (correct2) passed++;
|
||||
|
||||
// Ratio check at N=50
|
||||
long[] l50 = makeCipherList(50, 0);
|
||||
long s50 = slow.check(l50, l50);
|
||||
long f50 = fast.check(l50, l50);
|
||||
double ratio = (double)s50 / f50;
|
||||
total++;
|
||||
boolean ratioOk = ratio >= 5.0;
|
||||
System.out.printf("N=50 ratio=%.1fx >= 5x: %s%n", ratio, ratioOk ? "PASS" : "FAIL");
|
||||
if (ratioOk) passed++;
|
||||
|
||||
System.out.printf("%n%d/%d PASS%n", passed, total);
|
||||
if (passed < total) System.exit(1);
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue