Skip to content

Bench/attack surface - #1

Merged
umars28 merged 4 commits into
mainfrom
bench/attack-surface
Sep 7, 2026
Merged

Bench/attack surface#1
umars28 merged 4 commits into
mainfrom
bench/attack-surface

Conversation

@umars28

@umars28 umars28 commented Sep 7, 2026

Copy link
Copy Markdown
Member

No description provided.

Count distinct host kernel functions a runtime traverses while it holds root,
which is the window where every privileged operation happens and the one axis
published runtime comparisons do not cover.

mars reaches 1559 functions to start a container against crun's 2062 and
runc's 2351, and 484 against 920 and 1140 on exec. Namespace, pivot_root,
cgroup and capability work appears at parity, so the gap is not skipped work:
of the 843 functions runc reaches and mars does not, 250 are file and /proc
traversal and only 10 are thread or scheduler functions.

Widening the workload to cover seccomp exposed a blocker: mars refuses any
profile naming a syscall libseccomp cannot resolve, so the profile Podman and
CRI-O ship needs 59 names removed before it will start. runc and crun skip
unresolvable names and accept it unchanged.

The script asserts rather than trusts, because three separate methodology
faults each produced a plausible wrong answer first: per-PID trace filters
lose crun's child process, global tracing charges a runtime for its
neighbours, and a runtime that exits non-zero still yields a number.
mars refused to start under the profile Podman and CRI-O ship: 59 syscall
names had to be removed first, where runc and crun removed none.

The names that failed — bdflush, vm86, oldfstat, pciconfig_* — all look like
syscalls absent on aarch64, so the first reading was that libseccomp could not
resolve them. It already skipped unresolvable names; the failure came from
seccomp_rule_add, whose "the library doesn't permit the particular operation"
is libseccomp's wording for EACCES, returned when a rule's action equals the
filter's default action. These profiles deny by default and then spell out
denials for privileged syscalls, so every such rule is redundant by
construction. All 59 came from SCMP_ACT_ERRNO entries under an SCMP_ACT_ERRNO
default; none was an architecture problem.

Tolerate EACCES only when the rule's verdict equals the default, which cannot
change what the filter permits. Every other libseccomp error stays fatal, and
a deny rule under an allow-by-default filter carries the whole policy, differs
from the default and so never takes the tolerant path — asserted by test.

Splitting filter construction from load makes this testable without applying
seccomp to the test process. All three runtimes now share the unmodified
442-name profile, and the surface figures are restated from that run.
@umars28
umars28 merged commit 5da25f1 into main Sep 7, 2026
1 of 3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant