seccomp: from parent, find which system call caused child to die on SIGSYS

745 views Asked by At

I will describe my question in detail and forgive my unruly and crude please.

I had done some experiments on seccomp-BPF and signal handing. At beginning I followed this tutorial.

After adding SIGSYS signal handling (syscall-reporter.c) I can get some output below if the program execute some invalid system calls:

Looks like you need syscall fstat(5) too!

I want to know how can I print the system call name if I execute any other executable file by using execve function or other method.

I tried something like this: the main process(the process just named P0) firstly add the SIGCHLD signal handing, and fork a child process (named P1). P1 will add seccomp-bpf rules and perform seccomp action SCMP_ACT_TRAP on invalid system calls, then using the execve function replace the P1 with an executable file(named E). When execute E and it call some invalid system calls, the P1 will throw a SIGSYS signal and exit, then send SIGHLD signal to P0. Seems P0 can not get the invalid system call name from the ucontext_t after received SIGHLD signal.

The P1 can not print the system call name itself because

during an execve(2),the dispositions of handled ignals are reset to the default;

(from sigaction man-pages)

Sorry for my poor English.

1

There are 1 answers

0
Seirios On

I've come across the exact same problem while using libseccomp to sandbox a process launched via fork + execvp.

My solution involves doing ptrace on the child process. Essentially, the idea is to trace the child process and examine each signal it receives. While being traced, every signal received causes the child process to trap, and we can detect this (si_code == CLD_TRAPPED) in the SIGCHLD sigaction handler of the parent process.

When the signal received by the child process is SIGSYS, as triggered by the SECCOMP_RET_TRAP action of the seccomp syscall, we get the offending syscall number in the si_syscall member of siginfo_t. To get the name of the offending syscall, I use libseccomp's resolver, but you can use any other approach.

Specifically, after the fork and before execvp, the child process calls:

ptrace(PTRACE_TRACEME, 0, NULL, NULL);

Then, in the SIGCHLD sigaction handler of the parent process:

#include <seccomp.h>
#include <signal.h>
#include <sys/ptrace.h>

#ifndef SYS_SECCOMP
#define SYS_SECCOMP 1
#endif

void sigchld_sigaction(int sig, siginfo_t *info, void *ucontext) {
    if(info->si_code == CLD_TRAPPED) {
        siginfo_t cinfo;
        ptrace(PTRACE_GETSIGINFO, info->si_pid, NULL, &cinfo);
        if(cinfo.si_code == SYS_SECCOMP && cinfo.si_signo == SIGSYS) {
            char *sc_name = seccomp_syscall_resolve_num_arch(cinfo.si_arch,
                                                             cinfo.si_syscall);
            fprintf(stderr, "Looks like you need syscall %s(%d) too!\n",
                    sc_name, cinfo.si_syscall);
            free(sc_name);
            exit(EXIT_FAILURE);
        }
        ptrace(PTRACE_CONT, info->si_pid, NULL, NULL);
    }
}