Skip to content

unshare and a namespace clone fail with EPERM in a zone instead of killing it - #219

Merged
DevomB merged 1 commit into
mainfrom
zone-namespace-eperm
Oct 5, 2026
Merged

DevomB merged 1 commit into
mainfrom
zone-namespace-eperm

Conversation

@DevomB

@DevomB DevomB commented Oct 2, 2026

Copy link
Copy Markdown
Owner

The zone filter killed a process that called clone(2) with any CLONE_NEW* flag, or unshare(2). Firefox, Chromium, Electron programs and bubblewrap probe for user namespaces at start with clone(CLONE_NEWUSER) or unshare(CLONE_NEWUSER) and carry on when the answer is EPERM, so the kill left Firefox unable to start in a zone at all. EPERM is what the kernel tells an unprivileged caller, and what Docker's and Podman's default profiles answer.

The filter (seccomp.rs). clone with any bit of CLONE_NS_MASK returns ERRNO|EPERM; a plain clone is allowed as before. unshare joins REFUSED_SOFTLY with EPERM and stays in DENIED_RATIONALE, so no policy file can allow it, as with the set*id calls. setns is still killed and clone3 still gets ENOSYS; nothing else changes. One helper, soft_refusal, picks the errno or the trace notification for both the clone rule and the soft-refusal loop.

seccomp-trace (main.rs) answers a notified call through seccomp::soft_errno: EPERM for a namespace clone, the REFUSED_SOFTLY errno for the rest of that list, and ENOSYS for anything a zone is killed for (another architecture included). Both calls are printed with soft and fail with EPERM, as in a zone.

Probes. seccomp-test clone-newuser exits 7 when the call fails with EPERM (it returned 0 whatever happened and relied on the kill), and a new unshare-newuser probe does the same for unshare.

What the tests assert

  • Unit tests: a namespace clone (each of the seven flags, with junk in the high word) and unshare with any flags give ERRNO|EPERM; a plain clone is allowed; setns is killed; both stay denied and widened refuses them; under trace both are USER_NOTIF. For every denied or softly refused call, a namespace clone and TIOCSTI, the trace filter notifies and soft_errno matches what the zone filter returns, so the tracer cannot drift from the zone.
  • launcher.sh: the zone check runs unshare -U readlink /proc/self/ns/user and wants exit 1 with "Operation not permitted"; SIGSYS (159) fails it, and so does a printed user:[...], a namespace made. The clone-newuser probe wants 7, not 5. A new check runs both calls under seccomp-trace and wants KRYPTIK_SECCOMP_DENIED 56 clone soft, KRYPTIK_SECCOMP_DENIED 272 unshare soft and two -1 1 lines: a kernel may answer EPERM as well, and the names show the filter is what refuses.
  • adversarial.sh: unshare leaves the SIGSYS loop, which keeps 12 calls with setns first, and a second loop wants 7 from unshare-newuser and clone-newuser. Its precondition has already shown that the host lets the suite make a user namespace, so the EPERM is the filter's. The for ns in NEWUSER ... loop checks that isolate.rs declares kryptikd's own namespace set, not the filter, so it is unchanged.
  • boundary-checks.sh: unshare -U /bin/true in a zone wants exit 1 and "Operation not permitted" instead of 159, and the probe list wants clone-newuser 7, unshare-newuser 7, setns 5. Its comment no longer says the image's python lacks ctypes; build/recipes/python.sh requires it.

Docs. hardening.md and design/zone-policy-files.md say both calls fail with EPERM and why. decisions.md is untouched.

Conflicts to expect. #194 (cleanup-kryptikd) rewords the seccomp.rs comments this edits (the ERRNO note, REFUSED_SOFTLY, the trace comment the helper replaces) and renames the namespace-clone test. #197 (cleanup-compartment-suites) rewords the comments beside the unshare check, the filter probes and the adversarial SIGSYS loop. #208 (cleanup-docs) rewrites the same hardening.md paragraph, the "13 of these calls" line and the policy-file trace paragraph. #181 (clock-floor) and #182 (fuzz-schedule) touch main.rs elsewhere, and #178 and #184 add lines to boundary-checks.sh away from these. #214 (zone-input-checks) touches none of these files.

Not built or run on the laptop: CI compiles and tests kryptikd on glibc and musl and runs the three suites unprivileged, and a Distro run takes them as root on the target kernel.

…lling it, so Firefox, Chromium and bubblewrap can probe for user namespaces and start

Firefox, Chromium, Electron programs and bubblewrap probe for user
namespaces at start with clone(CLONE_NEWUSER) or unshare(CLONE_NEWUSER)
and carry on when the answer is EPERM: the kernel's answer to an
unprivileged caller, and the one Docker's and Podman's default profiles
give. A kill left Firefox unable to start in a zone at all.

unshare stays on the denied list, so no policy can allow it; setns is
killed and clone3 gets ENOSYS as before. seccomp-trace names both calls as
soft refusals and answers EPERM. The launcher, adversarial and boundary
suites expect the refusal and that no namespace is made, with an
unshare-newuser probe beside clone-newuser.
@DevomB
DevomB merged commit 1981d3f into main Oct 5, 2026
20 checks passed
@DevomB
DevomB deleted the zone-namespace-eperm branch October 7, 2026 03:48
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