seccomp: require child memory peek in the availability probe
ci / linux (gcc) (push) Canceled after 0s
ci / sanitizers (push) Canceled after 0s
ci / musl (push) Canceled after 0s
ci / macos (push) Canceled after 0s
ci / linux (clang) (push) Canceled after 4m58s

With Yama ptrace_scope=2 and no CAP_SYS_PTRACE, process_vm_readv and
/proc/<pid>/mem both fail, so sc_decide cannot read path strings and every
workspace write gets EACCES. The old probe only checked the USER_NOTIF
round trip, so CST_SANDBOX=seccomp and the forced-seccomp test pass looked
available and then failed on the first write.

sc_can_peek() forks a child, places a marker on its stack, and retries the
same sc_peek path. sc_probe() now needs that too, so those hosts fall back
to none (or keep landlock) instead of a silent deny-all sandbox.
This commit is contained in:
WHC
2026-10-07 01:39:08 +08:00
parent 1476f1774a
commit daa92a6cfb
4 changed files with 69 additions and 12 deletions
+42 -1
View File
@@ -391,6 +391,47 @@ static ssize_t sc_peek(int pid, uint64_t addr, void *out, size_t n) {
return k;
}
/* Path decisions need sc_peek on the sandboxed child. Yama
* kernel.yama.ptrace_scope=2 (and no CAP_SYS_PTRACE) denies that, so a
* listener-only probe would report seccomp available while every write
* fails with EACCES. */
static int sc_can_peek(void) {
static const char want[] = "cst-peek-ok";
int sp[2];
if (pipe2(sp, O_CLOEXEC) != 0)
return 0;
pid_t pid = fork();
if (pid == 0) {
char marker[] = "cst-peek-ok";
uint64_t addr = (uint64_t)(uintptr_t)marker;
close(sp[0]);
if (write(sp[1], &addr, sizeof(addr)) != (ssize_t)sizeof(addr))
_exit(1);
close(sp[1]);
pause();
_exit(0);
}
close(sp[1]);
if (pid < 0) {
close(sp[0]);
return 0;
}
uint64_t addr = 0;
int ok = 0;
if (read(sp[0], &addr, sizeof(addr)) == (ssize_t)sizeof(addr) && addr) {
char got[sizeof(want)];
if (sc_peek((int)pid, addr, got, sizeof(got)) == (ssize_t)sizeof(got) &&
memcmp(got, want, sizeof(got)) == 0)
ok = 1;
}
close(sp[0]);
kill(pid, SIGKILL);
int st = 0;
while (waitpid(pid, &st, 0) < 0 && errno == EINTR)
;
return ok;
}
static int sc_read_mem(int pid, uint64_t addr, void *out, size_t n) {
size_t got = 0;
while (got < n) {
@@ -862,7 +903,7 @@ static int sc_probe(void) {
int st = 0;
while (waitpid(pid, &st, 0) < 0 && errno == EINTR)
;
return ok && WIFEXITED(st) && WEXITSTATUS(st) == 0;
return ok && WIFEXITED(st) && WEXITSTATUS(st) == 0 && sc_can_peek();
}
#endif /* CST_HAVE_SECCOMP */