wave17: openssh-0001/2, strongswan-0001, gradle-0002, groovy-0001/2, make-0001, clojure-CLEAN + whitepaper 549/240
This commit is contained in:
parent
0f275c44af
commit
4221966e66
22 changed files with 1776 additions and 4 deletions
265
defects/strongswan/unit/StrongSwanProposalSelectTest.java
Normal file
265
defects/strongswan/unit/StrongSwanProposalSelectTest.java
Normal file
|
|
@ -0,0 +1,265 @@
|
|||
package unit;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* strongswan-0001: CWE-407 O(P²×T×A²) nested linear scans in proposal_select / select_algo
|
||||
*
|
||||
* Models the strongSwan proposal negotiation:
|
||||
* SLOW: nested ArrayList scans for per-transform algorithm matching (O(A₁×A₂) per type)
|
||||
* inside an outer O(P_c × P_s) proposal loop.
|
||||
* FAST: HashMap-indexed algorithm set per transform type → O(A₁+A₂)
|
||||
*
|
||||
* No JUnit — compile and run standalone.
|
||||
*/
|
||||
public class StrongSwanProposalSelectTest {
|
||||
|
||||
// ------------------------------------------------------------------
|
||||
// Data model: a "proposal" has transform types, each with a list of algs
|
||||
// Each alg is an int (algorithm id | key_size packed as long)
|
||||
// ------------------------------------------------------------------
|
||||
static class Proposal {
|
||||
// transform_type -> list of (alg_id << 16 | key_size) longs
|
||||
List<List<Long>> transforms; // indexed by transform type 0..T-1
|
||||
|
||||
Proposal(int numTypes) {
|
||||
transforms = new ArrayList<>();
|
||||
for (int i = 0; i < numTypes; i++) transforms.add(new ArrayList<>());
|
||||
}
|
||||
|
||||
void addAlg(int type, long algKey) {
|
||||
transforms.get(type).add(algKey);
|
||||
}
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------
|
||||
// SLOW: select_algo O(A₁ × A₂) — mirrors the C nested enumerator loop
|
||||
// Returns op count.
|
||||
// ------------------------------------------------------------------
|
||||
static long slowSelectAlgo(List<Long> localAlgs, List<Long> remoteAlgs) {
|
||||
long ops = 0;
|
||||
for (Long alg1 : localAlgs) {
|
||||
for (Long alg2 : remoteAlgs) {
|
||||
ops++;
|
||||
if (alg1.equals(alg2)) {
|
||||
return ops; // found — mirrors early break
|
||||
}
|
||||
}
|
||||
}
|
||||
return ops; // no match
|
||||
}
|
||||
|
||||
// SLOW: select_algos — iterate all T transform types
|
||||
static long slowSelectAlgos(Proposal local, Proposal remote) {
|
||||
long ops = 0;
|
||||
int T = local.transforms.size();
|
||||
for (int type = 0; type < T; type++) {
|
||||
ops += slowSelectAlgo(local.transforms.get(type), remote.transforms.get(type));
|
||||
}
|
||||
return ops;
|
||||
}
|
||||
|
||||
// SLOW: proposal_select — O(P_c × P_s) outer loop
|
||||
static long slowProposalSelect(List<Proposal> configured, List<Proposal> supplied) {
|
||||
long ops = 0;
|
||||
for (Proposal local : configured) {
|
||||
for (Proposal remote : supplied) {
|
||||
ops += slowSelectAlgos(local, remote);
|
||||
}
|
||||
}
|
||||
return ops;
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------
|
||||
// FAST: build HashMap per transform type from remote proposal, then O(1) lookup
|
||||
// ------------------------------------------------------------------
|
||||
static long fastSelectAlgo(List<Long> localAlgs, List<Long> remoteAlgs) {
|
||||
HashMap<Long, Boolean> remoteSet = new HashMap<>();
|
||||
long ops = 0;
|
||||
for (Long alg2 : remoteAlgs) {
|
||||
ops++;
|
||||
remoteSet.put(alg2, true);
|
||||
}
|
||||
for (Long alg1 : localAlgs) {
|
||||
ops++;
|
||||
if (remoteSet.containsKey(alg1)) {
|
||||
return ops; // found
|
||||
}
|
||||
}
|
||||
return ops;
|
||||
}
|
||||
|
||||
static long fastSelectAlgos(Proposal local, Proposal remote) {
|
||||
long ops = 0;
|
||||
int T = local.transforms.size();
|
||||
for (int type = 0; type < T; type++) {
|
||||
ops += fastSelectAlgo(local.transforms.get(type), remote.transforms.get(type));
|
||||
}
|
||||
return ops;
|
||||
}
|
||||
|
||||
static long fastProposalSelect(List<Proposal> configured, List<Proposal> supplied) {
|
||||
long ops = 0;
|
||||
for (Proposal local : configured) {
|
||||
for (Proposal remote : supplied) {
|
||||
ops += fastSelectAlgos(local, remote);
|
||||
}
|
||||
}
|
||||
return ops;
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------
|
||||
// Helpers: build proposal lists
|
||||
// ------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Build a list of P proposals, each with T transform types,
|
||||
* each type having A algorithms. Algorithms are unique per proposal
|
||||
* and offset so that no match occurs until the last comparison
|
||||
* (worst-case for the slow path).
|
||||
*/
|
||||
static List<Proposal> makeProposals(int P, int T, int A, int baseOffset) {
|
||||
List<Proposal> proposals = new ArrayList<>();
|
||||
for (int p = 0; p < P; p++) {
|
||||
Proposal prop = new Proposal(T);
|
||||
for (int t = 0; t < T; t++) {
|
||||
for (int a = 0; a < A; a++) {
|
||||
// unique algorithm id per (proposal, type, alg)
|
||||
long algKey = (long)(baseOffset + p * T * A + t * A + a) << 16;
|
||||
prop.addAlg(t, algKey);
|
||||
}
|
||||
}
|
||||
proposals.add(prop);
|
||||
}
|
||||
return proposals;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build supplied proposals that match on the LAST algorithm of LAST
|
||||
* proposal to force worst-case O(P²×T×A²).
|
||||
*/
|
||||
static List<Proposal> makeMatchingSupplied(List<Proposal> configured, int T, int A) {
|
||||
List<Proposal> supplied = new ArrayList<>();
|
||||
// clone configured proposals, put the matching alg last in each type
|
||||
for (Proposal cfg : configured) {
|
||||
Proposal sup = new Proposal(T);
|
||||
for (int t = 0; t < T; t++) {
|
||||
List<Long> cfgAlgs = cfg.transforms.get(t);
|
||||
// add non-matching alg first, then the matching alg last
|
||||
sup.addAlg(t, Long.MAX_VALUE - t); // no-match filler
|
||||
sup.addAlg(t, cfgAlgs.get(cfgAlgs.size() - 1)); // last cfg alg
|
||||
}
|
||||
supplied.add(sup);
|
||||
}
|
||||
return supplied;
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
int tests = 0, passed = 0;
|
||||
|
||||
// --- correctness: tiny case ---
|
||||
{
|
||||
tests++;
|
||||
int P = 2, T = 2, A = 3;
|
||||
List<Proposal> configured = makeProposals(P, T, A, 0);
|
||||
List<Proposal> supplied = makeMatchingSupplied(configured, T, A);
|
||||
|
||||
long slowOps = slowProposalSelect(configured, supplied);
|
||||
long fastOps = fastProposalSelect(configured, supplied);
|
||||
|
||||
// both should find a match (ops > 0); fast should use fewer ops
|
||||
if (slowOps > 0 && fastOps > 0 && fastOps <= slowOps) {
|
||||
passed++;
|
||||
System.out.println("PASS correctness P=" + P + " T=" + T + " A=" + A
|
||||
+ " slow_ops=" + slowOps + " fast_ops=" + fastOps);
|
||||
} else {
|
||||
System.out.println("FAIL correctness P=" + P + " T=" + T + " A=" + A
|
||||
+ " slow_ops=" + slowOps + " fast_ops=" + fastOps);
|
||||
}
|
||||
}
|
||||
|
||||
// --- no-match worst case ---
|
||||
{
|
||||
tests++;
|
||||
int P = 5, T = 3, A = 5;
|
||||
List<Proposal> configured = makeProposals(P, T, A, 0);
|
||||
// supplied has completely different algs — no match at all
|
||||
List<Proposal> supplied = makeProposals(P, T, A, 10000);
|
||||
|
||||
long slowOps = slowProposalSelect(configured, supplied);
|
||||
long fastOps = fastProposalSelect(configured, supplied);
|
||||
|
||||
double ratio = (double) slowOps / fastOps;
|
||||
boolean ok = ratio >= 2.0;
|
||||
if (ok) {
|
||||
passed++;
|
||||
System.out.printf("PASS no-match P=%d T=%d A=%d slow_ops=%d fast_ops=%d ratio=%.1fx%n",
|
||||
P, T, A, slowOps, fastOps, ratio);
|
||||
} else {
|
||||
System.out.printf("FAIL no-match P=%d T=%d A=%d slow_ops=%d fast_ops=%d ratio=%.1fx (need >=2x)%n",
|
||||
P, T, A, slowOps, fastOps, ratio);
|
||||
}
|
||||
}
|
||||
|
||||
// --- speedup benchmarks ---
|
||||
int[][] benchParams = {
|
||||
// {P, T, A} — worst case: no match until last comparison
|
||||
{10, 5, 8},
|
||||
{20, 5, 8},
|
||||
{30, 7, 10},
|
||||
{50, 7, 10},
|
||||
};
|
||||
for (int[] param : benchParams) {
|
||||
tests++;
|
||||
int P = param[0], T = param[1], A = param[2];
|
||||
List<Proposal> configured = makeProposals(P, T, A, 0);
|
||||
// no-match supplied: completely different algorithm ids
|
||||
List<Proposal> supplied = makeProposals(P, T, A, 100000);
|
||||
|
||||
long slowOps = slowProposalSelect(configured, supplied);
|
||||
long fastOps = fastProposalSelect(configured, supplied);
|
||||
|
||||
double ratio = (double) slowOps / fastOps;
|
||||
// At A=8 we expect ~A ratio for select_algo alone; with P² multiplied, ratio should be >=3x
|
||||
boolean ok = ratio >= 3.0;
|
||||
if (ok) {
|
||||
passed++;
|
||||
System.out.printf("PASS speedup P=%d T=%d A=%d slow_ops=%d fast_ops=%d ratio=%.1fx%n",
|
||||
P, T, A, slowOps, fastOps, ratio);
|
||||
} else {
|
||||
System.out.printf("FAIL speedup P=%d T=%d A=%d slow_ops=%d fast_ops=%d ratio=%.1fx (need >=3x)%n",
|
||||
P, T, A, slowOps, fastOps, ratio);
|
||||
}
|
||||
}
|
||||
|
||||
// --- large-scale attack simulation ---
|
||||
// Attacker sends 50 non-matching proposals, server has 30 proposals
|
||||
// This models a DoS amplification scenario
|
||||
{
|
||||
tests++;
|
||||
int Pc = 30, Ps = 50, T = 5, A = 8;
|
||||
List<Proposal> configured = makeProposals(Pc, T, A, 0);
|
||||
List<Proposal> supplied = makeProposals(Ps, T, A, 100000);
|
||||
|
||||
long slowOps = slowProposalSelect(configured, supplied);
|
||||
long fastOps = fastProposalSelect(configured, supplied);
|
||||
|
||||
double ratio = (double) slowOps / fastOps;
|
||||
// With A=8: slow does A² ops per transform (64), fast does 2A (16) → ratio ~= A/2 = 4x
|
||||
// The key point is the A² exponent is eliminated; require ratio >= 3x
|
||||
boolean ok = ratio >= 3.0;
|
||||
if (ok) {
|
||||
passed++;
|
||||
System.out.printf("PASS dos-scenario Pc=%d Ps=%d T=%d A=%d slow_ops=%d fast_ops=%d ratio=%.1fx%n",
|
||||
Pc, Ps, T, A, slowOps, fastOps, ratio);
|
||||
} else {
|
||||
System.out.printf("FAIL dos-scenario Pc=%d Ps=%d T=%d A=%d slow_ops=%d fast_ops=%d ratio=%.1fx (need >=3x)%n",
|
||||
Pc, Ps, T, A, slowOps, fastOps, ratio);
|
||||
}
|
||||
}
|
||||
|
||||
System.out.println("\n" + passed + "/" + tests + " PASS");
|
||||
if (passed != tests) System.exit(1);
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue