On Fri, Jan 17, 2025 at 10:34 AM Dmitry V. Levin ldv@strace.io wrote:
On Thu, Jan 16, 2025 at 04:55:39PM -0800, Eyal Birger wrote:
When attaching uretprobes to processes running inside docker, the attached process is segfaulted when encountering the retprobe.
The reason is that now that uretprobe is a system call the default seccomp filters in docker block it as they only allow a specific set of known syscalls. This is true for other userspace applications which use seccomp to control their syscall surface.
Since uretprobe is a "kernel implementation detail" system call which is not used by userspace application code directly, it is impractical and there's very little point in forcing all userspace applications to explicitly allow it in order to avoid crashing tracked processes.
Pass this systemcall through seccomp without depending on configuration.
Fixes: ff474a78cef5 ("uprobe: Add uretprobe syscall to speed up return probe") Reported-by: Rafael Buchbinder rafi@rbk.io Link: https://lore.kernel.org/lkml/CAHsH6Gs3Eh8DFU0wq58c_LF8A4_+o6z456J7BidmcVY2Aq... Cc: stable@vger.kernel.org Signed-off-by: Eyal Birger eyal.birger@gmail.com
The following reproduction script synthetically demonstrates the problem:
cat > /tmp/x.c << EOF
char *syscalls[] = { "write", "exit_group", "fstat", };
__attribute__((noinline)) int probed(void) { printf("Probed\n"); return 1; }
void apply_seccomp_filter(char **syscalls, int num_syscalls) { scmp_filter_ctx ctx;
ctx = seccomp_init(SCMP_ACT_KILL); for (int i = 0; i < num_syscalls; i++) { seccomp_rule_add(ctx, SCMP_ACT_ALLOW, seccomp_syscall_resolve_name(syscalls[i]), 0); } seccomp_load(ctx); seccomp_release(ctx);
}
int main(int argc, char *argv[]) { int num_syscalls = sizeof(syscalls) / sizeof(syscalls[0]);
apply_seccomp_filter(syscalls, num_syscalls); probed(); return 0;
} EOF
cat > /tmp/trace.bt << EOF uretprobe:/tmp/x:probed { printf("ret=%d\n", retval); } EOF
gcc -o /tmp/x /tmp/x.c -lseccomp
/usr/bin/bpftrace /tmp/trace.bt &
sleep 5 # wait for uretprobe attach /tmp/x
pkill bpftrace
rm /tmp/x /tmp/x.c /tmp/trace.bt
kernel/seccomp.c | 5 +++++ 1 file changed, 5 insertions(+)
diff --git a/kernel/seccomp.c b/kernel/seccomp.c index 385d48293a5f..10a55c9b5c18 100644 --- a/kernel/seccomp.c +++ b/kernel/seccomp.c @@ -1359,6 +1359,11 @@ int __secure_computing(const struct seccomp_data *sd) this_syscall = sd ? sd->nr : syscall_get_nr(current, current_pt_regs());
+#ifdef CONFIG_X86_64
if (unlikely(this_syscall == __NR_uretprobe) && !in_ia32_syscall())
return 0;
+#endif
switch (mode) { case SECCOMP_MODE_STRICT: __secure_computing_strict(this_syscall); /* may call do_exit */
This seems to be a hot fix to bypass some SECCOMP_RET_ERRNO filters.
It's a little broader than just SECCOMP_RET_ERRNO, but yes, this is a hotfix to avoid filtering this system call in seccomp.
The rationale is that this is not a userspace created system call - the kernel uses it to instrument the function - and the fact that it's a system call is just an implementation detail. Ideally, userspace wouldn't need to know or care about it.
However, this way it bypasses seccomp completely, including SECCOMP_RET_TRACE, making it invisible to strace --seccomp, and I wonder why do you want that.
It's a good question. I could move this check to both "strict" seccomp and after the BPF verdict is received, but before it's applied, but I fear this would make the fix more error prone, and way harder to backmerge. So I'm wondering whether supporting strace --seccomp-bpf for this particular syscall is a priority.
Eyal.