wave17: 562/240 — vim/qemu/tcl/nodejs/bun/httpd-0002/systemd/emacs
This commit is contained in:
parent
cce7ec653a
commit
dde5ec97fb
13 changed files with 887 additions and 4 deletions
225
defects/httpd/unit/HttpdProxyNoproxyTest.java
Normal file
225
defects/httpd/unit/HttpdProxyNoproxyTest.java
Normal file
|
|
@ -0,0 +1,225 @@
|
|||
package unit;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* HttpdProxyNoproxyTest — CWE-407 model test for Apache httpd httpd-0002.
|
||||
*
|
||||
* Models the defect in mod_proxy.c where set_proxy_exclude and
|
||||
* set_proxy_dirconn each scan the full existing array for duplicates before
|
||||
* inserting a new entry. With N directives the total cost is O(N²).
|
||||
*
|
||||
* Defective: ArrayList scanned with strcasecmp per directive → O(N²) total
|
||||
* Fixed: HashSet membership check per directive → O(N) total
|
||||
*
|
||||
* Parameters:
|
||||
* N = 500 directives (e.g. NoProxy entries in httpd.conf)
|
||||
*
|
||||
* Tests:
|
||||
* 1. testDefectiveCorrectness — dedup produces correct unique set
|
||||
* 2. testFixedCorrectness — dedup produces correct unique set
|
||||
* 3. testSlowOpCount — O(N²) comparisons measured
|
||||
* 4. testFastOpCount — O(N) comparisons measured
|
||||
* 5. testRatio — ratio >= 5x (expect ~249x at N=500)
|
||||
*/
|
||||
public class HttpdProxyNoproxyTest {
|
||||
|
||||
static final int N = 500; // number of NoProxy directives to load
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// Instrumentation
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
static long slowOps = 0;
|
||||
static long fastOps = 0;
|
||||
|
||||
static boolean instrumentedStrcasecmp(String a, String b) {
|
||||
slowOps++;
|
||||
return a.equalsIgnoreCase(b);
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// Model: defective implementation (APR array + linear dedup)
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
static class DefectiveNoproxyConfig {
|
||||
final List<String> entries = new ArrayList<>();
|
||||
|
||||
/** add() models set_proxy_exclude: linear scan then insert. */
|
||||
void add(String host) {
|
||||
for (int i = 0; i < entries.size(); i++) {
|
||||
if (instrumentedStrcasecmp(entries.get(i), host)) {
|
||||
return; // duplicate, skip
|
||||
}
|
||||
}
|
||||
entries.add(host.toLowerCase(Locale.ROOT));
|
||||
}
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// Model: fixed implementation (hash set for dedup)
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
static class FixedNoproxyConfig {
|
||||
final List<String> entries = new ArrayList<>();
|
||||
final Set<String> seen = new HashSet<>();
|
||||
|
||||
/** add() uses a hash set for O(1) dedup. */
|
||||
void add(String host) {
|
||||
fastOps++;
|
||||
String lower = host.toLowerCase(Locale.ROOT);
|
||||
if (seen.add(lower)) {
|
||||
entries.add(lower);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// Helpers
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Generate N distinct hostnames + N/2 duplicates interleaved.
|
||||
* Total directives = N + N/2 = 1.5*N; unique entries = N.
|
||||
*/
|
||||
static List<String> buildDirectives() {
|
||||
List<String> dirs = new ArrayList<>(N + N / 2);
|
||||
for (int i = 0; i < N; i++) {
|
||||
dirs.add("host-" + i + ".example.com");
|
||||
}
|
||||
// interleave N/2 duplicates (every other original entry, uppercased
|
||||
// to exercise case-insensitive comparison)
|
||||
for (int i = 0; i < N / 2; i++) {
|
||||
dirs.add("HOST-" + (i * 2) + ".EXAMPLE.COM");
|
||||
}
|
||||
return dirs;
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// Assert helpers
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
static void assertTrue(String msg, boolean cond) {
|
||||
if (!cond) throw new AssertionError("FAIL: " + msg);
|
||||
}
|
||||
|
||||
static void assertEquals(String msg, Object expected, Object actual) {
|
||||
if (!Objects.equals(expected, actual))
|
||||
throw new AssertionError("FAIL: " + msg
|
||||
+ " expected=" + expected + " actual=" + actual);
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// Test 1: defective correctness
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
static void testDefectiveCorrectness() {
|
||||
List<String> dirs = buildDirectives();
|
||||
DefectiveNoproxyConfig cfg = new DefectiveNoproxyConfig();
|
||||
slowOps = 0;
|
||||
for (String d : dirs) cfg.add(d);
|
||||
|
||||
assertEquals("defective unique count", N, cfg.entries.size());
|
||||
// Verify no duplicates remain
|
||||
Set<String> unique = new HashSet<>(cfg.entries);
|
||||
assertEquals("defective no duplicate entries", N, unique.size());
|
||||
System.out.println("[PASS] testDefectiveCorrectness entries=" + cfg.entries.size());
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// Test 2: fixed correctness
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
static void testFixedCorrectness() {
|
||||
List<String> dirs = buildDirectives();
|
||||
FixedNoproxyConfig cfg = new FixedNoproxyConfig();
|
||||
fastOps = 0;
|
||||
for (String d : dirs) cfg.add(d);
|
||||
|
||||
assertEquals("fixed unique count", N, cfg.entries.size());
|
||||
Set<String> unique = new HashSet<>(cfg.entries);
|
||||
assertEquals("fixed no duplicate entries", N, unique.size());
|
||||
System.out.println("[PASS] testFixedCorrectness entries=" + cfg.entries.size());
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// Test 3: slow (defective) op count is O(N²)
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
static void testSlowOpCount() {
|
||||
List<String> dirs = buildDirectives();
|
||||
DefectiveNoproxyConfig cfg = new DefectiveNoproxyConfig();
|
||||
slowOps = 0;
|
||||
for (String d : dirs) cfg.add(d);
|
||||
long slow = slowOps;
|
||||
|
||||
// With N unique entries + N/2 duplicates, total comparisons are at
|
||||
// least N*(N-1)/2 (triangular number for unique insertions alone).
|
||||
long minExpected = (long) N * (N - 1) / 2;
|
||||
assertTrue(
|
||||
"slow ops (" + slow + ") >= N*(N-1)/2 (" + minExpected + ")",
|
||||
slow >= minExpected
|
||||
);
|
||||
System.out.println("[PASS] testSlowOpCount slowOps=" + slow
|
||||
+ " minExpected=" + minExpected);
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// Test 4: fast (fixed) op count is O(N)
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
static void testFastOpCount() {
|
||||
List<String> dirs = buildDirectives();
|
||||
FixedNoproxyConfig cfg = new FixedNoproxyConfig();
|
||||
fastOps = 0;
|
||||
for (String d : dirs) cfg.add(d);
|
||||
long fast = fastOps;
|
||||
|
||||
// Every directive costs exactly 1 hash probe (our fastOps counter),
|
||||
// so fastOps == total directives = N + N/2.
|
||||
long totalDirs = N + (long) N / 2;
|
||||
assertEquals("fast ops equals total directive count", totalDirs, fast);
|
||||
System.out.println("[PASS] testFastOpCount fastOps=" + fast);
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// Test 5: ratio >= 5x (actual is ~249x at N=500)
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
static void testRatio() {
|
||||
List<String> dirs = buildDirectives();
|
||||
|
||||
DefectiveNoproxyConfig defCfg = new DefectiveNoproxyConfig();
|
||||
slowOps = 0;
|
||||
for (String d : dirs) defCfg.add(d);
|
||||
long slow = slowOps;
|
||||
|
||||
FixedNoproxyConfig fixCfg = new FixedNoproxyConfig();
|
||||
fastOps = 0;
|
||||
for (String d : dirs) fixCfg.add(d);
|
||||
long fast = fastOps;
|
||||
|
||||
double ratio = (double) slow / fast;
|
||||
System.out.printf("[INFO] slowOps=%d fastOps=%d ratio=%.1fx%n",
|
||||
slow, fast, ratio);
|
||||
assertTrue(
|
||||
"ratio (" + ratio + ") >= 5.0x (N=" + N + ")",
|
||||
ratio >= 5.0
|
||||
);
|
||||
System.out.printf("[PASS] testRatio ratio=%.1fx%n", ratio);
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// Main
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
public static void main(String[] args) {
|
||||
System.out.println("=== HttpdProxyNoproxyTest N=" + N + " ===");
|
||||
testDefectiveCorrectness();
|
||||
testFixedCorrectness();
|
||||
testSlowOpCount();
|
||||
testFastOpCount();
|
||||
testRatio();
|
||||
System.out.println("=== ALL TESTS PASSED ===");
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue