Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix SIGCONT handling on threads blocked in syscalls #3874

Merged
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 @@ -1330,6 +1330,7 @@ set(BASIC_TESTS
sigaltstack
sigchld_interrupt_signal
sigcont
sigcont_threaded
sigframe_grow_stack
sighandler_bad_rsp_sigsegv
sighandler_fork
Expand Down
2 changes: 1 addition & 1 deletion src/RecordTask.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1169,7 +1169,7 @@ void RecordTask::emulate_SIGCONT() {
// All threads in the process are resumed.
for (Task* t : thread_group()->task_set()) {
auto rt = static_cast<RecordTask*>(t);
LOG(debug) << "setting " << tid << " to NOT_STOPPED due to SIGCONT";
LOG(debug) << "setting " << rt->tid << " to NOT_STOPPED due to SIGCONT";
rt->clear_stashed_group_stop();
rt->emulated_stop_pending = false;
rt->emulated_stop_type = NOT_STOPPED;
Expand Down
8 changes: 4 additions & 4 deletions src/Scheduler.cc
Original file line number Diff line number Diff line change
Expand Up @@ -318,10 +318,10 @@ bool Scheduler::is_task_runnable(RecordTask* t, WaitAggregator& wait_aggregator,
}

if (t->emulated_stop_type != NOT_STOPPED) {
if (t->is_signal_pending(SIGCONT)) {
// We have to do this here. RecordTask::signal_delivered can't always
// do it because if we don't PTRACE_CONT the task, we'll never see the
// SIGCONT.
if (t->is_stopped() && t->is_signal_pending(SIGCONT)) {
// We have to do this here. RecordTask::signal_delivered can't do it
// in the case where t->is_stopped(), because if we don't PTRACE_CONT
// the task, we'll never see the SIGCONT.
t->emulate_SIGCONT();
// We shouldn't run any user code since there is at least one signal
// pending.
Expand Down
63 changes: 63 additions & 0 deletions src/test/sigcont_threaded.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
/* -*- Mode: C; tab-width: 8; c-basic-offset: 2; indent-tabs-mode: nil; -*- */

#include "util.h"

static void *child_process_extra_thread(__attribute__((unused)) void *extra_thread) {
int r;

// Slap in a sched_yield or two here so that the parent process is going to be
// blocked in pthread_join.
sched_yield();
sched_yield();

// Now, stop ourselves. We'll be unstopped by the parent process.
r = kill(gettid(), SIGSTOP);
test_assert(r == 0);

// Now allow self to exit, and the thread-group-leader can continue.
return NULL;
}

static void child_process(void) {
pthread_t extra_thread;
int r;
// Spawn an additional thread
r = pthread_create(&extra_thread, NULL, child_process_extra_thread, NULL);
test_assert(r == 0);

// Wait for the child thread we made. It will send SIGSTOP to the process.
r = pthread_join(extra_thread, NULL);
test_assert(r == 0);
}

static void parent_process(pid_t pid) {
int wait_status, r;
pid_t wpid;

// Wait for the child process to have sent itself SIGSTOP
wpid = waitpid(pid, &wait_status, WUNTRACED);
test_assert(wpid == pid);
test_assert(WIFSTOPPED(wait_status));
test_assert(WSTOPSIG(wait_status) == SIGSTOP);

// Let it continue
r = kill(pid, SIGCONT);
test_assert(r == 0);

// Now the child process should actually exit
wpid = waitpid(pid, &wait_status, 0);
test_assert(wpid == pid);
test_assert(WIFEXITED(wait_status));
}

int main(void) {
pid_t pid = fork();
test_assert(pid != -1);
if (pid == 0) {
child_process();
} else {
parent_process(pid);
atomic_puts("EXIT-SUCCESS");
}
return 0;
}