Skip to content

Remove functions that are not async-signal-safe from signal handler. #3035

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

Merged
merged 1 commit into from
Jul 9, 2025
Merged
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
49 changes: 38 additions & 11 deletions judge/runguard.cc
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,7 @@ pid_t child_pid = -1;

static volatile sig_atomic_t received_SIGCHLD = 0;
static volatile sig_atomic_t received_signal = -1;
static volatile sig_atomic_t error_in_signalhandler = 0;

int child_pipefd[3][2];
int child_redirfd[3];
Expand Down Expand Up @@ -192,7 +193,7 @@ struct option const long_opts[] = {
void warning( const char *, ...) __attribute__((format (printf, 1, 2)));
void verbose( const char *, ...) __attribute__((format (printf, 1, 2)));
void error(int, const char *, ...) __attribute__((format (printf, 2, 3)));
void write_meta(const char*, const char *, ...) __attribute__((format (printf, 2, 3)));
void write_meta(const char *, const char *, ...) __attribute__((format (printf, 2, 3)));

void warning(const char *format, ...)
{
Expand Down Expand Up @@ -226,6 +227,25 @@ void verbose(const char *format, ...)
va_end(ap);
}

// These functions are called from signal handlers, so they
// must only call async-signal-safe functions.
// write() is async-signal-safe, printf and variants are not.
void verbose_from_signalhandler(const char* msg)
{
if (!be_quiet && be_verbose) {
write(STDERR_FILENO, msg, strlen(msg));
}
}

void warning_from_signalhandler(const char* msg)
{
if (!be_quiet) {
// Do not include timing here, as it wouldn't be safe from a signalhandler.
// TODO: Consider rewriting using clock_gettime in the future.
write(STDERR_FILENO, msg, strlen(msg));
}
}

void error(int errnum, const char *format, ...)
{
// Silently ignore errors that happen while handling other errors.
Expand Down Expand Up @@ -674,43 +694,47 @@ void terminate(int sig)
sigact.sa_handler = SIG_DFL;
sigact.sa_flags = 0;
if ( sigemptyset(&sigact.sa_mask)!=0 ) {
warning("could not initialize signal mask");
warning_from_signalhandler("could not initialize signal mask");
}
if ( sigaction(SIGTERM,&sigact,nullptr)!=0 ) {
warning("could not restore signal handler");
warning_from_signalhandler("could not restore signal handler");
}
if ( sigaction(SIGALRM,&sigact,nullptr)!=0 ) {
warning("could not restore signal handler");
warning_from_signalhandler("could not restore signal handler");
}

if ( sig==SIGALRM ) {
if (runpipe_pid > 0) {
warning("sending SIGUSR1 to runpipe with pid %d", runpipe_pid);
warning_from_signalhandler("sending SIGUSR1 to runpipe");
kill(runpipe_pid, SIGUSR1);
}

walllimit_reached |= hard_timelimit;
warning("timelimit exceeded (hard wall time): aborting command");
warning_from_signalhandler("timelimit exceeded (hard wall time): aborting command");
} else {
warning("received signal %d: aborting command",sig);
warning_from_signalhandler("received signal: aborting command");
}

received_signal = sig;

/* First try to kill graciously, then hard.
Don't report an already exited process as error. */
verbose("sending SIGTERM");
verbose_from_signalhandler("sending SIGTERM");
if ( kill(-child_pid,SIGTERM)!=0 && errno!=ESRCH ) {
error(errno,"sending SIGTERM to command");
warning_from_signalhandler("error sending SIGTERM to command");
error_in_signalhandler = 1;
return;
}

/* Prefer nanosleep over sleep because of higher resolution and
it does not interfere with signals. */
nanosleep(&killdelay,nullptr);

verbose("sending SIGKILL");
verbose_from_signalhandler("sending SIGKILL");
if ( kill(-child_pid,SIGKILL)!=0 && errno!=ESRCH ) {
error(errno,"sending SIGKILL to command");
warning_from_signalhandler("error sending SIGKILL to command");
error_in_signalhandler = 1;
return;
}

/* Wait another while to make sure the process is killed by now. */
Expand Down Expand Up @@ -1465,6 +1489,9 @@ int main(int argc, char **argv)

int r = pselect(nfds+1, &readfds, nullptr, NULL, NULL, &emptymask);
if ( r==-1 && errno!=EINTR ) error(errno,"waiting for child data");
if (error_in_signalhandler) {
error(errno, "error in signal handler, exiting");
}

if ( received_SIGCHLD || received_signal == SIGALRM ) {
pid_t pid;
Expand Down
Loading