Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -1656,6 +1656,7 @@ set(TESTS_WITH_PROGRAM
mprotect_growsdown
mprotect_syscallbuf_overflow
mutex_pi_stress
negative_syscall_switch
nested_detach_wait
nested_detach_kill_stuck
nested_release_signal
Expand Down
2 changes: 1 addition & 1 deletion src/RecordSession.cc
Original file line number Diff line number Diff line change
Expand Up @@ -451,9 +451,9 @@ void RecordSession::handle_seccomp_traced_syscall(RecordTask* t,
// Don't continue yet. At the next iteration of record_step, we'll
// enter syscall_state_changed and that will trigger a continue to
// the syscall exit.
last_task_switchable = ALLOW_SWITCH;
step_state->continue_type = RecordSession::DONT_CONTINUE;
if (!process_syscall_entry(t, step_state, result, t->arch())) {
last_task_switchable = ALLOW_SWITCH;
return;
}
*did_enter_syscall = true;
Expand Down
47 changes: 47 additions & 0 deletions src/test/negative_syscall_switch.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
/* -*- Mode: C; tab-width: 8; c-basic-offset: 2; indent-tabs-mode: nil; -*- */

#include "util.h"

/* The kernel skips a syscall with a negative number after its seccomp stop.
rr must record that syscall's entry before it runs another thread, so that
the main thread's store to `flag` is ordered before what do_thread does
after reading it. The .run file uses --always-switch so that rr switches
threads wherever it allows a switch. */

static int thread_running;
static int flag;

static void* do_thread(__attribute__((unused)) void* p) {
__atomic_store_n(&thread_running, 1, __ATOMIC_RELEASE);
while (!__atomic_load_n(&flag, __ATOMIC_ACQUIRE)) {
sched_yield();
}
/* do_thread's first event after it reads `flag` is the entry of this
unbuffered syscall. */
unbufferable_syscall(RR_getpid, 0, 0, 0);
return NULL;
}

int main(void) {
for (int i = 0; i < 20; ++i) {
pthread_t thread;
__atomic_store_n(&thread_running, 0, __ATOMIC_RELAXED);
__atomic_store_n(&flag, 0, __ATOMIC_RELAXED);
test_assert(0 == pthread_create(&thread, NULL, do_thread, NULL));
while (!__atomic_load_n(&thread_running, __ATOMIC_ACQUIRE)) {
sched_yield();
}
if (i & 1) {
/* One more syscall stop of the main thread changes whether rr has
stopped do_thread inside sched_yield() or after it returned when
the main thread makes the negative syscall. */
unbufferable_syscall(RR_getpid, 0, 0, 0);
}
__atomic_store_n(&flag, 1, __ATOMIC_RELEASE);
test_assert(-1 == syscall(-10) && ENOSYS == errno);
test_assert(0 == pthread_join(thread, NULL));
}

atomic_puts("EXIT-SUCCESS");
return 0;
}
4 changes: 4 additions & 0 deletions src/test/negative_syscall_switch.run
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
source `dirname $0`/util.sh

RECORD_ARGS="--always-switch"
compare_test EXIT-SUCCESS
Loading