systemd-0004: seccomp_load_syscall_filter_set() in src/shared/seccomp-util.c uses strv_contains(added, name) — O(|added|) linear scan — inside NULSTR_FOREACH over ~537 KNOWN syscalls. On x86_64 (3 arches): ~484,000 string comparisons per service start with SeccompFilter=. Sibling function seccomp_load_syscall_filter_set_raw() already uses hashmap_contains for O(1); this function was left behind. Fix: build Set* from added strv before the NULSTR_FOREACH loop. MEDIUM severity. dbus-0001: bus_client_policy_optimize() in bus/policy.c iterates R rules and for each blanket deny/allow calls remove_rules_by_type_up_to() which scans backward from current position to head — O(R^2) total per new connection creation. At R=100 rules (realistic system bus): ~10,000 comparisons per connect. Fix: single O(R) reverse pass tracking last-seen blanket per rule type. MEDIUM. dbus 5-MOAD summary: MOAD-0001: dbus-0001 DEFECT (policy optimize O(R^2)) MOAD-0002: CLEAN (BusContext is standard daemon context, not a god object) MOAD-0003: CLEAN (single-threaded event loop, no thread-local state) MOAD-0004: CLEAN (_dbus_verbose is no-op in production builds) MOAD-0005: CLEAN (pending_activations hash table coalesces duplicate requests)
117 lines
4.1 KiB
Java
117 lines
4.1 KiB
Java
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<String> 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<String> 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<String> known = new ArrayList<>();
|
|
for (int i = 0; i < K; i++) {
|
|
known.add("sys_" + i);
|
|
}
|
|
|
|
// Build added strv: first A syscalls already covered
|
|
List<String> added_strv = new ArrayList<>();
|
|
Set<String> 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);
|
|
}
|
|
}
|