Skip to content

Commit

Permalink
selftests/seccomp: build and pass on arm64
Browse files Browse the repository at this point in the history
Changing arm64 syscalls is done via a specific register set, more like s390
than like arm (specific ptrace call) and x86 (part of general registers).
Since (restarting) poll doesn't exist on arm64, switch to using nanosleep
for testing restart_syscall. And since it looks like the syscall ABI is
inconsistent on arm-compat, so we must work around it (and document it) in
the test.

Signed-off-by: Kees Cook <keescook@chromium.org>
Signed-off-by: Shuah Khan <shuahkh@osg.samsung.com>
  • Loading branch information
Kees Cook authored and Shuah Khan committed Oct 16, 2015
1 parent 1f78dda commit 256d0af
Showing 1 changed file with 49 additions and 22 deletions.
71 changes: 49 additions & 22 deletions tools/testing/selftests/seccomp/seccomp_bpf.c
Original file line number Diff line number Diff line change
Expand Up @@ -19,15 +19,16 @@
#include <linux/prctl.h>
#include <linux/ptrace.h>
#include <linux/seccomp.h>
#include <poll.h>
#include <pthread.h>
#include <semaphore.h>
#include <signal.h>
#include <stddef.h>
#include <stdbool.h>
#include <string.h>
#include <time.h>
#include <linux/elf.h>
#include <sys/uio.h>
#include <sys/utsname.h>

#define _GNU_SOURCE
#include <unistd.h>
Expand Down Expand Up @@ -1247,8 +1248,8 @@ void change_syscall(struct __test_metadata *_metadata,
ret = ptrace(PTRACE_GETREGSET, tracee, NT_PRSTATUS, &iov);
EXPECT_EQ(0, ret);

#if defined(__x86_64__) || defined(__i386__) || defined(__aarch64__) || \
defined(__powerpc__) || defined(__s390__)
#if defined(__x86_64__) || defined(__i386__) || defined(__powerpc__) || \
defined(__s390__)
{
regs.SYSCALL_NUM = syscall;
}
Expand All @@ -1262,6 +1263,18 @@ void change_syscall(struct __test_metadata *_metadata,
EXPECT_EQ(0, ret);
}

#elif defined(__aarch64__)
# ifndef NT_ARM_SYSTEM_CALL
# define NT_ARM_SYSTEM_CALL 0x404
# endif
{
iov.iov_base = &syscall;
iov.iov_len = sizeof(syscall);
ret = ptrace(PTRACE_SETREGSET, tracee, NT_ARM_SYSTEM_CALL,
&iov);
EXPECT_EQ(0, ret);
}

#else
ASSERT_EQ(1, 0) {
TH_LOG("How is the syscall changed on this architecture?");
Expand All @@ -1272,6 +1285,8 @@ void change_syscall(struct __test_metadata *_metadata,
if (syscall == -1)
regs.SYSCALL_RET = 1;

iov.iov_base = &regs;
iov.iov_len = sizeof(regs);
ret = ptrace(PTRACE_SETREGSET, tracee, NT_PRSTATUS, &iov);
EXPECT_EQ(0, ret);
}
Expand Down Expand Up @@ -2005,20 +2020,25 @@ TEST(syscall_restart)
BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, __NR_read, 5, 0),
BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, __NR_exit, 4, 0),
BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, __NR_rt_sigreturn, 3, 0),
BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, __NR_poll, 4, 0),
BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, __NR_nanosleep, 4, 0),
BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, __NR_restart_syscall, 4, 0),

/* Allow __NR_write for easy logging. */
BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, __NR_write, 0, 1),
BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_ALLOW),
BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_KILL),
BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_TRACE|0x100), /* poll */
BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_TRACE|0x200), /* restart */
/* The nanosleep jump target. */
BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_TRACE|0x100),
/* The restart_syscall jump target. */
BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_TRACE|0x200),
};
struct sock_fprog prog = {
.len = (unsigned short)ARRAY_SIZE(filter),
.filter = filter,
};
#if defined(__arm__)
struct utsname utsbuf;
#endif

ASSERT_EQ(0, pipe(pipefd));

Expand All @@ -2027,10 +2047,7 @@ TEST(syscall_restart)
if (child_pid == 0) {
/* Child uses EXPECT not ASSERT to deliver status correctly. */
char buf = ' ';
struct pollfd fds = {
.fd = pipefd[0],
.events = POLLIN,
};
struct timespec timeout = { };

/* Attach parent as tracer and stop. */
EXPECT_EQ(0, ptrace(PTRACE_TRACEME));
Expand All @@ -2054,10 +2071,11 @@ TEST(syscall_restart)
TH_LOG("Failed to get sync data from read()");
}

/* Start poll to be interrupted. */
/* Start nanosleep to be interrupted. */
timeout.tv_sec = 1;
errno = 0;
EXPECT_EQ(1, poll(&fds, 1, -1)) {
TH_LOG("Call to poll() failed (errno %d)", errno);
EXPECT_EQ(0, nanosleep(&timeout, NULL)) {
TH_LOG("Call to nanosleep() failed (errno %d)", errno);
}

/* Read final sync from parent. */
Expand All @@ -2082,14 +2100,14 @@ TEST(syscall_restart)
ASSERT_EQ(0, ptrace(PTRACE_CONT, child_pid, NULL, 0));
ASSERT_EQ(1, write(pipefd[1], ".", 1));

/* Wait for poll() to start. */
/* Wait for nanosleep() to start. */
ASSERT_EQ(child_pid, waitpid(child_pid, &status, 0));
ASSERT_EQ(true, WIFSTOPPED(status));
ASSERT_EQ(SIGTRAP, WSTOPSIG(status));
ASSERT_EQ(PTRACE_EVENT_SECCOMP, (status >> 16));
ASSERT_EQ(0, ptrace(PTRACE_GETEVENTMSG, child_pid, NULL, &msg));
ASSERT_EQ(0x100, msg);
EXPECT_EQ(__NR_poll, get_syscall(_metadata, child_pid));
EXPECT_EQ(__NR_nanosleep, get_syscall(_metadata, child_pid));

/* Might as well check siginfo for sanity while we're here. */
ASSERT_EQ(0, ptrace(PTRACE_GETSIGINFO, child_pid, NULL, &info));
Expand All @@ -2100,7 +2118,7 @@ TEST(syscall_restart)
/* Verify signal delivery came from child (seccomp-triggered). */
EXPECT_EQ(child_pid, info.si_pid);

/* Interrupt poll with SIGSTOP (which we'll need to handle). */
/* Interrupt nanosleep with SIGSTOP (which we'll need to handle). */
ASSERT_EQ(0, kill(child_pid, SIGSTOP));
ASSERT_EQ(0, ptrace(PTRACE_CONT, child_pid, NULL, 0));
ASSERT_EQ(child_pid, waitpid(child_pid, &status, 0));
Expand All @@ -2110,7 +2128,7 @@ TEST(syscall_restart)
ASSERT_EQ(0, ptrace(PTRACE_GETSIGINFO, child_pid, NULL, &info));
EXPECT_EQ(getpid(), info.si_pid);

/* Restart poll with SIGCONT, which triggers restart_syscall. */
/* Restart nanosleep with SIGCONT, which triggers restart_syscall. */
ASSERT_EQ(0, kill(child_pid, SIGCONT));
ASSERT_EQ(0, ptrace(PTRACE_CONT, child_pid, NULL, 0));
ASSERT_EQ(child_pid, waitpid(child_pid, &status, 0));
Expand All @@ -2124,16 +2142,25 @@ TEST(syscall_restart)
ASSERT_EQ(SIGTRAP, WSTOPSIG(status));
ASSERT_EQ(PTRACE_EVENT_SECCOMP, (status >> 16));
ASSERT_EQ(0, ptrace(PTRACE_GETEVENTMSG, child_pid, NULL, &msg));

ASSERT_EQ(0x200, msg);
ret = get_syscall(_metadata, child_pid);
#if defined(__arm__)
/* FIXME: ARM does not expose true syscall in registers. */
EXPECT_EQ(__NR_poll, ret);
#else
EXPECT_EQ(__NR_restart_syscall, ret);
/*
* FIXME:
* - native ARM registers do NOT expose true syscall.
* - compat ARM registers on ARM64 DO expose true syscall.
*/
ASSERT_EQ(0, uname(&utsbuf));
if (strncmp(utsbuf.machine, "arm", 3) == 0) {
EXPECT_EQ(__NR_nanosleep, ret);
} else
#endif
{
EXPECT_EQ(__NR_restart_syscall, ret);
}

/* Write again to end poll. */
/* Write again to end test. */
ASSERT_EQ(0, ptrace(PTRACE_CONT, child_pid, NULL, 0));
ASSERT_EQ(1, write(pipefd[1], "!", 1));
EXPECT_EQ(0, close(pipefd[1]));
Expand Down

0 comments on commit 256d0af

Please sign in to comment.