Skip to content

Commit

Permalink
Handle clone syscalls being aborted with an errno by a user seccomp f…
Browse files Browse the repository at this point in the history
…ilter
  • Loading branch information
rocallahan committed Aug 1, 2018
1 parent ac88079 commit 721065d
Show file tree
Hide file tree
Showing 6 changed files with 64 additions and 5 deletions.
1 change: 1 addition & 0 deletions CMakeLists.txt
Expand Up @@ -837,6 +837,7 @@ set(BASIC_TESTS
scm_rights
scratch_read
seccomp
seccomp_clone_fail
seccomp_desched
seccomp_kill_exit
seccomp_null
Expand Down
8 changes: 4 additions & 4 deletions src/Event.h
Expand Up @@ -231,10 +231,10 @@ struct SyscallEvent {
Switchable switchable;
// True when this syscall was restarted after a signal interruption.
bool is_restart;
// True when this syscall failed during preparation. This includes syscalls
// that were interrupted by SIGSYS via seccomp, and clone system calls that
// failed. These system calls failed no matter what the syscall-result
// register says.
// True when this syscall failed during preparation: syscall entry events
// that were interrupted by a user seccomp filter forcing SIGSYS or errno,
// and clone system calls that failed. These system calls failed no matter
// what the syscall-result register says.
bool failed_during_preparation;
// Syscall is being emulated via PTRACE_SYSEMU.
bool in_sysemu;
Expand Down
3 changes: 3 additions & 0 deletions src/RecordSession.cc
Expand Up @@ -430,6 +430,9 @@ static void handle_seccomp_errno(RecordTask* t,

if (!t->is_in_untraced_syscall()) {
t->push_syscall_event(syscallno);
// Note that the syscall failed. prepare_clone() needs to know
// this during replay of the syscall entry.
t->ev().Syscall().failed_during_preparation = true;
note_entering_syscall(t);
}

Expand Down
2 changes: 1 addition & 1 deletion src/preload/preload_interface.h
Expand Up @@ -525,7 +525,7 @@ struct syscallbuf_hdr {
*/
volatile uint8_t in_sigprocmask_critical_section;
/* Nonzero when the syscall was aborted during preparation without doing
* anything */
* anything. This is set when a user seccomp filter forces a SIGSYS. */
volatile uint8_t failed_during_preparation;

struct syscallbuf_record recs[0];
Expand Down
3 changes: 3 additions & 0 deletions src/replay_syscall.cc
Expand Up @@ -193,6 +193,9 @@ static TraceTaskEvent read_task_trace_event(ReplayTask* t,
template <typename Arch> static void prepare_clone(ReplayTask* t) {
const TraceFrame& trace_frame = t->current_trace_frame();

// We're being called with the syscall entry event, so we can't inspect the result
// of the syscall exit to see whether the clone succeeded (that event can happen
// much later, even after the spawned task has run).
if (trace_frame.event().Syscall().failed_during_preparation) {
/* creation failed, nothing special to do */
return;
Expand Down
52 changes: 52 additions & 0 deletions src/test/seccomp_clone_fail.c
@@ -0,0 +1,52 @@
/* -*- Mode: C; tab-width: 8; c-basic-offset: 2; indent-tabs-mode: nil; -*- */

#include "util.h"

static void install_filter(void) {
struct sock_filter filter[] = {
/* Load system call number from 'seccomp_data' buffer into
accumulator */
BPF_STMT(BPF_LD | BPF_W | BPF_ABS, offsetof(struct seccomp_data, nr)),
/* Jump forward 1 instruction if system call number
is not SYS_clone */
BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, SYS_clone, 0, 1),
/* Error out with ENOTTY */
BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_ERRNO | (ENOTTY & SECCOMP_RET_DATA)),
/* Destination of system call number mismatch: allow other
system calls */
BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_ALLOW)
};
struct sock_fprog prog = {
.len = (unsigned short)(sizeof(filter) / sizeof(filter[0])),
.filter = filter,
};
int ret;

ret = syscall(RR_seccomp, SECCOMP_SET_MODE_FILTER, 0, &prog);
if (ret == -1 && errno == ENOSYS) {
ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog);
}
test_assert(ret == 0);
}

static int child(__attribute__((unused)) void* arg) {
/* NOT REACHED */
syscall(SYS_exit, 77);
return 0;
}

int main(void) {
const size_t stack_size = 1 << 20;
void* stack = mmap(NULL, stack_size, PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);

test_assert(0 == prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0));
install_filter();

pid_t ret = clone(child, stack + stack_size, CLONE_VM | CLONE_THREAD | CLONE_SIGHAND,
NULL, NULL, NULL, NULL);
test_assert(ret == -1 && errno == ENOTTY);

atomic_puts("EXIT-SUCCESS");
return 0;
}

0 comments on commit 721065d

Please sign in to comment.