Hi, On Tue, May 8, 2018 at 4:34 PM, Michael Ellerman <m...@ellerman.id.au> wrote: > Mathieu Malaterre <ma...@debian.org> writes: > >> Hi there, >> >> Quick question (I have not investigate root cause): is support for >> seccomp complete on ppc32 ? > > Doesn't look like it does it :) > >> $ make KBUILD_OUTPUT=/tmp/kselftest TARGETS=seccomp kselftest >> ... >> seccomp_bpf.c:1804:TRACE_syscall.ptrace_syscall_dropped:Expected 1 (1) >> == syscall(286) (4294967295) >> TRACE_syscall.ptrace_syscall_dropped: Test failed at step #13 >> [ FAIL ] TRACE_syscall.ptrace_syscall_dropped >> ... >> [ RUN ] global.get_metadata >> seccomp_bpf.c:2880:global.get_metadata:Expected 0 (0) == seccomp(1, 2, >> &prog) (4294967295) >> seccomp_bpf.c:2892:global.get_metadata:Expected 1 (1) == >> read(pipefd[0], &buf, 1) (0) >> global.get_metadata: Test terminated by assertion >> [ FAIL ] global.get_metadata > > I'm not sure sorry. > > That could be a test case bug, hard to say without looking at the > details.
I've reduced the test case to the attached file. Does that help, or should I reduce it some more ? $ gcc -m32 -o ptrace_syscall_dropped ptrace_syscall_dropped.c running it as root: # ./ptrace_syscall_dropped [==========] Running 1 tests from 1 test cases. [ RUN ] TRACE_syscall.ptrace_syscall_dropped ptrace_syscall_dropped.c:298:TRACE_syscall.ptrace_syscall_dropped:Expected 1 (1) == syscall(286) (4294967295) TRACE_syscall.ptrace_syscall_dropped: Test failed at step #13 [ FAIL ] TRACE_syscall.ptrace_syscall_dropped [==========] 0 / 1 tests passed. [ FAILED ] Thanks
#include <linux/filter.h> #include <sys/prctl.h> #include <sys/ptrace.h> #include <sys/user.h> #include <linux/ptrace.h> #include <linux/seccomp.h> #include <linux/elf.h> #include <sys/uio.h> #define _GNU_SOURCE #include <unistd.h> #include <sys/syscall.h> #include "tools/testing/selftests/kselftest_harness.h" #define IS_SECCOMP_EVENT(status) ((status >> 16) == PTRACE_EVENT_SECCOMP) static bool tracer_running; static void tracer_stop(int sig) { tracer_running = false; } typedef void tracer_func_t(struct __test_metadata *_metadata, pid_t tracee, int status, void *args); static void start_tracer(struct __test_metadata *_metadata, int fd, pid_t tracee, tracer_func_t tracer_func, void *args, bool ptrace_syscall) { int ret = -1; struct sigaction action = { .sa_handler = tracer_stop, }; /* Allow external shutdown. */ tracer_running = true; ASSERT_EQ(0, sigaction(SIGUSR1, &action, NULL)); errno = 0; while (ret == -1 && errno != EINVAL) ret = ptrace(PTRACE_ATTACH, tracee, NULL, 0); ASSERT_EQ(0, ret) { kill(tracee, SIGKILL); } /* Wait for attach stop */ wait(NULL); ret = ptrace(PTRACE_SETOPTIONS, tracee, NULL, ptrace_syscall ? PTRACE_O_TRACESYSGOOD : PTRACE_O_TRACESECCOMP); ASSERT_EQ(0, ret) { TH_LOG("Failed to set PTRACE_O_TRACESECCOMP"); kill(tracee, SIGKILL); } ret = ptrace(ptrace_syscall ? PTRACE_SYSCALL : PTRACE_CONT, tracee, NULL, 0); ASSERT_EQ(0, ret); /* Unblock the tracee */ ASSERT_EQ(1, write(fd, "A", 1)); ASSERT_EQ(0, close(fd)); /* Run until we're shut down. Must assert to stop execution. */ while (tracer_running) { int status; if (wait(&status) != tracee) continue; if (WIFSIGNALED(status) || WIFEXITED(status)) /* Child is dead. Time to go. */ return; /* Check if this is a seccomp event. */ ASSERT_EQ(!ptrace_syscall, IS_SECCOMP_EVENT(status)); tracer_func(_metadata, tracee, status, args); ret = ptrace(ptrace_syscall ? PTRACE_SYSCALL : PTRACE_CONT, tracee, NULL, 0); ASSERT_EQ(0, ret); } /* Directly report the status of our test harness results. */ syscall(__NR_exit, _metadata->passed ? EXIT_SUCCESS : EXIT_FAILURE); } /* Common tracer setup/teardown functions. */ static void cont_handler(int num) { } static pid_t setup_trace_fixture(struct __test_metadata *_metadata, tracer_func_t func, void *args, bool ptrace_syscall) { char sync; int pipefd[2]; pid_t tracer_pid; pid_t tracee = getpid(); /* Setup a pipe for clean synchronization. */ ASSERT_EQ(0, pipe(pipefd)); /* Fork a child which we'll promote to tracer */ tracer_pid = fork(); ASSERT_LE(0, tracer_pid); signal(SIGALRM, cont_handler); if (tracer_pid == 0) { close(pipefd[0]); start_tracer(_metadata, pipefd[1], tracee, func, args, ptrace_syscall); syscall(__NR_exit, 0); } close(pipefd[1]); prctl(PR_SET_PTRACER, tracer_pid, 0, 0, 0); read(pipefd[0], &sync, 1); close(pipefd[0]); return tracer_pid; } static void teardown_trace_fixture(struct __test_metadata *_metadata, pid_t tracer) { if (tracer) { int status; /* * Extract the exit code from the other process and * adopt it for ourselves in case its asserts failed. */ ASSERT_EQ(0, kill(tracer, SIGUSR1)); ASSERT_EQ(tracer, waitpid(tracer, &status, 0)); if (WEXITSTATUS(status)) _metadata->passed = 0; } } # define ARCH_REGS struct pt_regs # define SYSCALL_NUM gpr[0] # define SYSCALL_RET gpr[3] /* Architecture-specific syscall fetching routine. */ static int get_syscall(struct __test_metadata *_metadata, pid_t tracee) { ARCH_REGS regs; struct iovec iov; iov.iov_base = ®s; iov.iov_len = sizeof(regs); EXPECT_EQ(0, ptrace(PTRACE_GETREGSET, tracee, NT_PRSTATUS, &iov)) { TH_LOG("PTRACE_GETREGSET failed"); return -1; } return regs.SYSCALL_NUM; } /* Architecture-specific syscall changing routine. */ static void change_syscall(struct __test_metadata *_metadata, pid_t tracee, int syscall) { int ret; ARCH_REGS regs; struct iovec iov; iov.iov_base = ®s; iov.iov_len = sizeof(regs); ret = ptrace(PTRACE_GETREGSET, tracee, NT_PRSTATUS, &iov); EXPECT_EQ(0, ret) {} { regs.SYSCALL_NUM = syscall; } /* If syscall is skipped, change return value. */ if (syscall == -1) regs.SYSCALL_RET = EPERM; iov.iov_base = ®s; iov.iov_len = sizeof(regs); ret = ptrace(PTRACE_SETREGSET, tracee, NT_PRSTATUS, &iov); EXPECT_EQ(0, ret); } static void tracer_syscall(struct __test_metadata *_metadata, pid_t tracee, int status, void *args) { int ret; unsigned long msg; /* Make sure we got the right message. */ ret = ptrace(PTRACE_GETEVENTMSG, tracee, NULL, &msg); EXPECT_EQ(0, ret); /* Validate and take action on expected syscalls. */ switch (msg) { case 0x1002: /* change getpid to getppid. */ EXPECT_EQ(__NR_getpid, get_syscall(_metadata, tracee)); change_syscall(_metadata, tracee, __NR_getppid); break; case 0x1003: /* skip gettid. */ EXPECT_EQ(__NR_gettid, get_syscall(_metadata, tracee)); change_syscall(_metadata, tracee, -1); break; case 0x1004: /* do nothing (allow getppid) */ EXPECT_EQ(__NR_getppid, get_syscall(_metadata, tracee)); break; default: EXPECT_EQ(0, msg) { TH_LOG("Unknown PTRACE_GETEVENTMSG: 0x%lx", msg); kill(tracee, SIGKILL); } } } static void tracer_ptrace(struct __test_metadata *_metadata, pid_t tracee, int status, void *args) { int ret, nr; unsigned long msg; static bool entry; /* Make sure we got an empty message. */ ret = ptrace(PTRACE_GETEVENTMSG, tracee, NULL, &msg); EXPECT_EQ(0, ret); EXPECT_EQ(0, msg); /* The only way to tell PTRACE_SYSCALL entry/exit is by counting. */ entry = !entry; if (!entry) return; nr = get_syscall(_metadata, tracee); if (nr == __NR_getpid) change_syscall(_metadata, tracee, __NR_getppid); if (nr == __NR_openat) change_syscall(_metadata, tracee, -1); } FIXTURE_DATA(TRACE_syscall) { struct sock_fprog prog; pid_t tracer, mytid, mypid, parent; }; FIXTURE_SETUP(TRACE_syscall) { struct sock_filter filter[] = { BPF_STMT(BPF_LD|BPF_W|BPF_ABS, offsetof(struct seccomp_data, nr)), BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, __NR_getpid, 0, 1), BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_TRACE | 0x1002), BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, __NR_gettid, 0, 1), BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_TRACE | 0x1003), BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, __NR_getppid, 0, 1), BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_TRACE | 0x1004), BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_ALLOW), }; memset(&self->prog, 0, sizeof(self->prog)); self->prog.filter = malloc(sizeof(filter)); ASSERT_NE(NULL, self->prog.filter); memcpy(self->prog.filter, filter, sizeof(filter)); self->prog.len = (unsigned short)ARRAY_SIZE(filter); /* Prepare some testable syscall results. */ self->mytid = syscall(__NR_gettid); ASSERT_GT(self->mytid, 0); ASSERT_NE(self->mytid, 1) { TH_LOG("Running this test as init is not supported. :)"); } self->mypid = getpid(); ASSERT_GT(self->mypid, 0); ASSERT_EQ(self->mytid, self->mypid); self->parent = getppid(); ASSERT_GT(self->parent, 0); ASSERT_NE(self->parent, self->mypid); /* Launch tracer. */ self->tracer = setup_trace_fixture(_metadata, tracer_syscall, NULL, false); } FIXTURE_TEARDOWN(TRACE_syscall) { teardown_trace_fixture(_metadata, self->tracer); if (self->prog.filter) free(self->prog.filter); } TEST_F(TRACE_syscall, ptrace_syscall_dropped) { /* Swap SECCOMP_RET_TRACE tracer for PTRACE_SYSCALL tracer. */ teardown_trace_fixture(_metadata, self->tracer); self->tracer = setup_trace_fixture(_metadata, tracer_ptrace, NULL, true); /* Tracer should skip the open syscall, resulting in EPERM. */ EXPECT_EQ(EPERM, syscall(__NR_openat)); } TEST_HARNESS_MAIN