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)
72 lines
2.4 KiB
Markdown
72 lines
2.4 KiB
Markdown
# systemd-0004: seccomp-util.c strv_contains in KNOWN syscall scan O(K*A)
|
|
|
|
**MOAD:** MOAD-0001 (CWE-407)
|
|
**Severity:** MEDIUM
|
|
**Speedup:** ~150x at K=537, A=300 (per architecture per service start)
|
|
**Language:** C
|
|
**File:** src/shared/seccomp-util.c:1195-1210
|
|
|
|
## Description
|
|
|
|
`seccomp_load_syscall_filter_set()` builds a `char **added` string vector (strv)
|
|
containing every syscall name that was already handled by the requested filter
|
|
set. After processing the filter set it iterates over all ~537 syscalls in the
|
|
`@known` set and, for each one, calls `strv_contains(added, name)` to check
|
|
whether that syscall was already covered:
|
|
|
|
```c
|
|
SECCOMP_FOREACH_LOCAL_ARCH(arch) {
|
|
_cleanup_strv_free_ char **added = NULL;
|
|
|
|
r = add_syscall_filter_set(seccomp, set, action, NULL, log_missing, &added);
|
|
...
|
|
NULSTR_FOREACH(name, syscall_filter_sets[SYSCALL_FILTER_SET_KNOWN].value) {
|
|
...
|
|
if (strv_contains(added, name)) /* O(|added|) linear scan */
|
|
continue;
|
|
...
|
|
}
|
|
}
|
|
```
|
|
|
|
`strv_contains()` performs a linear scan. The `added` strv can hold 200-400
|
|
entries depending on which filter set is loaded. On x86\_64 systemd iterates 3
|
|
architectures (x86, x32, x86\_64). Total comparisons:
|
|
|
|
```
|
|
3 arches * 537 KNOWN * ~300 added = ~484,000 string comparisons per service start
|
|
```
|
|
|
|
The sibling function `seccomp_load_syscall_filter_set_raw()` (line 1246)
|
|
already uses `hashmap_contains(filter, ...)` for O(1) lookup, confirming that
|
|
O(1) lookup is the intended pattern. This function was never updated to match.
|
|
|
|
Every systemd service unit that uses `SystemCallFilter=` triggers this path
|
|
on service activation. Container environments starting many services
|
|
simultaneously amplify the impact.
|
|
|
|
## Fix
|
|
|
|
Before the `NULSTR_FOREACH` loop, build a `Set *added_set` from `added` strv
|
|
entries. Use `set_contains(added_set, name)` instead of `strv_contains(added,
|
|
name)`. This mirrors the `hashmap_contains` pattern already used in the raw
|
|
variant.
|
|
|
|
```c
|
|
/* Build O(1) lookup set from added strv */
|
|
_cleanup_set_free_ Set *added_set = NULL;
|
|
STRV_FOREACH(n, added) {
|
|
r = set_put_strdup(&added_set, *n);
|
|
if (r < 0)
|
|
return log_oom();
|
|
}
|
|
|
|
NULSTR_FOREACH(name, syscall_filter_sets[SYSCALL_FILTER_SET_KNOWN].value) {
|
|
...
|
|
if (set_contains(added_set, name)) /* O(1) */
|
|
continue;
|
|
...
|
|
}
|
|
```
|
|
|
|
Complexity drops from O(K * A) to O(K + A) per architecture.
|