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
+13 -10
View File
@@ -66,16 +66,19 @@ every create / remove / rename / link / chmod / chown / utimes / xattr call
stop until the supervisor has read the path from the child's memory,
resolved it (cwd, dirfd, `..`, symlinks, `/proc/self`, `/dev/stdout`) and
answered: continue when it lands in the allowed places, `EACCES` otherwise.
Reads, exec and writes to already-open files are not checked. Background
jobs that outlive their shell are no longer the agent's descendants: with
Yama `ptrace_scope` ≥ 1 (common on desktop distros, absent on Android) the
supervisor cannot read their memory and their checked writes fail with
`EACCES`; once the run has finished they get `ENOSYS`. This keeps
mistakes out of the rest of the disk but is not a security boundary: the
kernel reads the path again after the check, so a hostile multi-threaded
program can swap it in between. The availability check runs in a
throwaway child, so a platform seccomp policy that kills the caller cannot
take the agent down.
Reads, exec and writes to already-open files are not checked. The probe also
checks that the parent can peek a direct child's memory (`process_vm_readv`
or `/proc/<pid>/mem`); with Yama `ptrace_scope=2` and no `CAP_SYS_PTRACE`
that fails, so seccomp is not selected (forced `CST_SANDBOX=seccomp` then
falls back to unavailable). Background jobs that outlive their shell are no
longer the agent's descendants: with Yama `ptrace_scope` ≥ 1 (common on
desktop distros, absent on Android) the supervisor cannot read their memory
and their checked writes fail with `EACCES`; once the run has finished they
get `ENOSYS`. This keeps mistakes out of the rest of the disk but is not a
security boundary: the kernel reads the path again after the check, so a
hostile multi-threaded program can swap it in between. The availability
check runs in a throwaway child, so a platform seccomp policy that kills the
caller cannot take the agent down.
## Terminal notes