systemd+dbus: 5-MOAD scan; systemd-0004 CWE-407 seccomp strv O(K*A), dbus-0001 CWE-407 policy optimize O(R^2)

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)
This commit is contained in:
russell@unturf.com 2026-04-03 15:53:52 -04:00
parent a89cc53fed
commit decacb5dbd
8 changed files with 681 additions and 0 deletions

View file

@ -0,0 +1,38 @@
# systemd deeper scan — 2026-04-03
Source: https://github.com/systemd/systemd (depth=1)
Focus: src/shared/seccomp-util.c (missed in 2026-03-31 scan)
Prior defects:
- systemd-0001: strv_extend_strv dedup O(N^2) (src/basic/strv.c) — HIGH
- systemd-0002: unit_file_get_list states filter O(U*S) (src/shared/install.c) — MEDIUM
- systemd-0003: dbus-cgroup BPF filter strv dedup O(N^2) (src/core/dbus-cgroup.c) — LOW-MEDIUM
## MOAD-0001 (CWE-407): DEFECT — systemd-0004
`src/shared/seccomp-util.c` `seccomp_load_syscall_filter_set()`:
After processing the requested filter set (building `char **added` strv of
covered syscall names), the function iterates all ~537 KNOWN syscalls and for
each calls `strv_contains(added, name)` which is an O(|added|) linear scan.
This produces O(K * A) comparisons where:
- K = 537 KNOWN syscalls on x86\_64
- A = filter set size (200-400 for common sets like @default, @system-service)
On x86\_64 systemd processes 3 architectures (x86, x32, x86\_64):
```
3 * 537 * ~300 = ~484,000 string comparisons per service activation with SeccompFilter=
```
The sibling function `seccomp_load_syscall_filter_set_raw()` already uses
`hashmap_contains(filter, id)` for O(1) lookup. This function was left behind
when the raw variant was optimized.
**Fix:** build a `Set *added_set` from the `added` strv before the
`NULSTR_FOREACH` loop, then use `set_contains(added_set, name)` for O(1).
Complexity drops from O(K*A) to O(K+A) per architecture.
See `systemd-0004-seccomp-strv-to-set.patch` and `TICKET.md`.
## MOADs 0002-0005: carried from 2026-03-31 scan (all CLEAN)

View file

@ -0,0 +1,44 @@
# UNDF: UNDF-PENDING
--- a/src/shared/seccomp-util.c
+++ b/src/shared/seccomp-util.c
@@ -1178,7 +1178,7 @@ int seccomp_load_syscall_filter_set(uint32_t default_action, const SyscallFilter
SECCOMP_FOREACH_LOCAL_ARCH(arch) {
_cleanup_(seccomp_releasep) scmp_filter_ctx seccomp = NULL;
- _cleanup_strv_free_ char **added = NULL;
+ _cleanup_strv_free_ char **added = NULL;
+ _cleanup_set_free_ Set *added_set = NULL;
log_trace("Operating on architecture: %s", seccomp_arch_to_string(arch));
@@ -1188,10 +1188,20 @@ int seccomp_load_syscall_filter_set(uint32_t default_action, const SyscallFilter
if (r < 0)
return log_debug_errno(r, "Failed to add filter set: %m");
+ /* Build O(1) lookup set from the added strv to avoid O(K*A) quadratic
+ * scan in the NULSTR_FOREACH loop below. The sibling function
+ * seccomp_load_syscall_filter_set_raw() already uses hashmap_contains
+ * for O(1); this brings the named-set path in line with it. */
+ if (default_action != default_action_override) {
+ char **n;
+ STRV_FOREACH(n, added) {
+ r = set_put_strdup(&added_set, *n);
+ if (r < 0)
+ return log_oom();
+ }
+ }
+
if (default_action != default_action_override)
NULSTR_FOREACH(name, syscall_filter_sets[SYSCALL_FILTER_SET_KNOWN].value) {
int id;
id = sym_seccomp_syscall_resolve_name(name);
if (id < 0)
continue;
/* Ignore the syscall if it was already handled above */
- if (strv_contains(added, name))
+ if (set_contains(added_set, name))
continue;
r = sym_seccomp_rule_add_exact(seccomp, default_action, id, 0);