package unit; import java.util.*; /** * TomcatTest — CWE-407 benchmark for tomcat-0001 * * Models ReplicationValve.registerReplicationSession() O(N²) ArrayList.contains() * cross-context session dedup vs. O(N) LinkedHashSet.add(). * * Real code (java/org/apache/catalina/ha/tcp/ReplicationValve.java:265-275): * if (!sessions.contains(session)) { // O(n) ArrayList scan → O(n²) total * sessions.add(session); * } * * Fix: LinkedHashSet — O(1) add, no contains() needed. */ public class TomcatTest { static void bench(String label, Runnable slow, Runnable fast, long sOps, long fOps) { 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 speedup = fMs > 0 ? (double) sMs / fMs : 0; System.out.printf(" %-54s slow:%4dms (%,d ops) fast:%4dms (%,d ops) speedup:%.0fx%n", label, sMs, sOps, fMs, fOps, speedup); } /** Returns total equals() comparisons performed (slow path) */ static long slowRegister(int N, int requests) { long ops = 0; for (int req = 0; req < requests; req++) { List sessions = new ArrayList<>(); for (int s = 0; s < N; s++) { // simulate contains(): walk entire list boolean found = false; for (int i = 0; i < sessions.size(); i++) { ops++; if (sessions.get(i).equals(s)) { found = true; break; } } if (!found) sessions.add(s); } } return ops; } /** Returns total operations (O(1) each — fast path) */ static long fastRegister(int N, int requests) { long ops = 0; for (int req = 0; req < requests; req++) { LinkedHashSet sessions = new LinkedHashSet<>(N * 2); for (int s = 0; s < N; s++) { ops++; // O(1) set.add() sessions.add(s); } } return ops; } public static void main(String[] args) { System.out.println("TomcatTest — tomcat-0001: ReplicationValve cross-context ArrayList.contains() → LinkedHashSet"); System.out.println(); System.out.println(" [ReplicationValve.registerReplicationSession() cross-context session dedup]"); int[][] cases = {{20, 50000}, {50, 20000}, {100, 10000}}; for (int[] c : cases) { int N = c[0], R = c[1]; bench( String.format("N=%d sessions per request, %,d requests", N, R), () -> slowRegister(N, R), () -> fastRegister(N, R), (long) N * (N - 1) / 2 * R, (long) N * R ); } System.out.println(); System.out.println("Defect : java/org/apache/catalina/ha/tcp/ReplicationValve.java:265-275"); System.out.println(" sessions.contains(session) — ArrayList O(n) per call → O(n²) total"); System.out.println("Fix : LinkedHashSet — O(1) add, no contains() check needed"); System.out.println("Ticket : tomcat-0001-replicationvalve-crosscontext-arraylist-contains.md"); System.out.println(); int pass = 0; long s0 = slowRegister(100, 100), f0 = fastRegister(100, 100); assert s0 > f0 * 10 : "tomcat-0001 expected >10x; slow=" + s0 + " fast=" + f0; pass++; System.out.printf("%d/1 PASS — tomcat-0001: CWE-407 in Tomcat ReplicationValve session dedup%n", pass); System.out.printf("Hotpath: every clustered request with N cross-context DeltaSessions registered%n"); } }