package defects.systemd_0004; import org.junit.Test; import static org.junit.Assert.*; import java.util.*; /** * Unit test demonstrating systemd-0004 CWE-407: * seccomp_load_syscall_filter_set() uses strv_contains (O(N) linear scan) * inside NULSTR_FOREACH over ~537 KNOWN syscalls, producing O(K*A) behavior. * * The sibling function already uses hashmap/set for O(1) lookup. * This test proves O(K*A) vs O(K+A) operation count at realistic scales. */ public class TestSeccompStrvToSet { /** Simulates strv_contains: linear scan of a list for a name. */ static int strv_contains(List added, String name) { for (String s : added) { if (s.equals(name)) return 1; // found } return 0; // not found } /** Simulates set_contains: O(1) hash lookup. */ static int set_contains(Set added_set, String name) { return added_set.contains(name) ? 1 : 0; } /** * Measures operation count for the O(K*A) defect vs O(K+A) fix. * * @param K number of KNOWN syscalls (e.g. 537 on x86_64) * @param A number of syscalls already added to filter (e.g. 300 for @default) */ static long[] measureOps(int K, int A) { // Build KNOWN syscall list (K entries) List known = new ArrayList<>(); for (int i = 0; i < K; i++) { known.add("sys_" + i); } // Build added strv: first A syscalls already covered List added_strv = new ArrayList<>(); Set added_set = new HashSet<>(); for (int i = 0; i < A; i++) { added_strv.add("sys_" + i); added_set.add("sys_" + i); } // Defect: O(K*A) - strv_contains inside loop over KNOWN long ops_defect = 0; for (String name : known) { // strv_contains does up to A comparisons for (String s : added_strv) { ops_defect++; if (s.equals(name)) break; } } // Fix: O(K+A) - set_contains inside loop over KNOWN // Build set: A ops long ops_fix = A; // set construction // Loop: K * O(1) lookups for (String name : known) { ops_fix++; // one hash lookup per iteration if (added_set.contains(name)) continue; } return new long[]{ops_defect, ops_fix}; } @Test public void testRealisticScale() { // K=537 (x86_64 KNOWN syscalls), A=300 (@default filter set size) int K = 537, A = 300; long[] ops = measureOps(K, A); long defect = ops[0], fix = ops[1]; System.out.printf("K=%d, A=%d: defect=%,d ops, fix=%,d ops, speedup=%.1fx%n", K, A, defect, fix, (double) defect / fix); // The defect should be dramatically more expensive assertTrue("Defect should have > 10x more ops than fix at K=537, A=300", defect > fix * 10); // Compute expected speedup double speedup = (double) defect / fix; assertTrue("Speedup should be > 50x at K=537, A=300", speedup > 50.0); } @Test public void testSmallScale() { // Even at small scale (K=100, A=50) the defect shows clearly long[] ops = measureOps(100, 50); long defect = ops[0], fix = ops[1]; System.out.printf("K=100, A=50: defect=%,d ops, fix=%,d ops, speedup=%.1fx%n", 100, 50, defect, fix, (double) defect / fix); assertTrue("Defect always more expensive than fix", defect > fix); } @Test public void testMultipleArches() { // x86_64 runs 3 architectures (x86, x32, x86_64) int arches = 3, K = 537, A = 300; long total_defect = 0, total_fix = 0; for (int i = 0; i < arches; i++) { long[] ops = measureOps(K, A); total_defect += ops[0]; total_fix += ops[1]; } System.out.printf("3 arches: defect=%,d ops, fix=%,d ops, speedup=%.1fx%n", total_defect, total_fix, (double) total_defect / total_fix); assertTrue("3-arch total: defect > 100x fix", total_defect > total_fix * 100); } }