Skip to content

Commit c7db4fc

Browse files
nyhwkozaczuk
authored andcommitted
alarm(): fix crash when alarm is rearmed from alarm handler
It is quite common for applications to set an alarm() and when it expires, set a new alarm() from the signal handler. For example, this is what the ping application does, and before this patch it often crashes after a random number of times doing this. The problem starts with alarm() wanting to know which thread ran it. It needs this information so when an alarm happens it can interrupt a system call sleeping in this specific thread. But unfortunately, when alarm() runs from a signal handler (as described above), in OSv this signal handler is a separate transient thread. Before this patch, we remember *this* thread, and when the alarm next happens we try to wake that thread, which no longer exists at that time. The result is of course undefined, and depending on what else happened since, may result in a crash. The solution in this patch is for alarm() not to change the old owner thread if called from a signal handler thread. We *hope* that the previous one is still relevant, and it will be true for most applications such as ping. This workaround is kind of ugly, but it does fix the ping crash described in issue 1073. Fixes #1073. Signed-off-by: Nadav Har'El <[email protected]> Message-Id: <[email protected]>
1 parent 3fdce4a commit c7db4fc

File tree

1 file changed

+19
-2
lines changed

1 file changed

+19
-2
lines changed

libc/signal.cc

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -496,11 +496,26 @@ void itimer::work()
496496
}
497497
}
498498

499+
// alarm() wants to know which thread ran it, so when the alarm happens it
500+
// can interrupt a system call sleeping on this thread. But there's a special
501+
// case: if alarm() is called in a signal handler (which currently in OSv is
502+
// a separate thread), this probably means the alarm was re-set after the
503+
// previous alarm expired. In that case we obviously don't want to remember
504+
// the signal handler thread (which will go away almost immediately). What
505+
// we'll do in the is_signal_handler() case is to just keep remembering the
506+
// old owner thread, hoping it is still relevant...
507+
static bool is_signal_handler(){
508+
// must be the same name used in kill() above
509+
return sched::thread::current()->name() == "signal_handler";
510+
}
511+
499512
void itimer::cancel()
500513
{
501514
_due = _no_alarm;
502515
_interval = decltype(_interval)::zero();
503-
_owner_thread = nullptr;
516+
if (!is_signal_handler()) {
517+
_owner_thread = nullptr;
518+
}
504519
_cond.wake_one();
505520
}
506521

@@ -513,7 +528,9 @@ void itimer::set_value(const struct timeval *tv)
513528
_started = true;
514529
}
515530
_due = now + tv->tv_sec * 1_s + tv->tv_usec * 1_us;
516-
_owner_thread = sched::thread::current();
531+
if (!is_signal_handler()) {
532+
_owner_thread = sched::thread::current();
533+
}
517534
_cond.wake_one();
518535
}
519536

0 commit comments

Comments
 (0)