* ptrace_syscall_dropped.c:298:TRACE_syscall.ptrace_syscall_dropped:Expected 1 (1) == syscall(286) (4294967295)
@ 2018-05-26 19:50 Mathieu Malaterre
0 siblings, 0 replies; only message in thread
From: Mathieu Malaterre @ 2018-05-26 19:50 UTC (permalink / raw)
To: Michael Ellerman; +Cc: linuxppc-dev
[-- Attachment #1: Type: text/plain, Size: 1595 bytes --]
Hi,
On Tue, May 8, 2018 at 4:34 PM, Michael Ellerman <mpe@ellerman.id.au> wrote:
> Mathieu Malaterre <malat@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
[-- Attachment #2: ptrace_syscall_dropped.c --]
[-- Type: text/x-csrc, Size: 7743 bytes --]
#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
^ permalink raw reply [flat|nested] only message in thread
only message in thread, other threads:[~2018-05-26 19:50 UTC | newest]
Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2018-05-26 19:50 ptrace_syscall_dropped.c:298:TRACE_syscall.ptrace_syscall_dropped:Expected 1 (1) == syscall(286) (4294967295) Mathieu Malaterre
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.