From: Willy Tarreau Date: Tue, 21 Jun 2016 14:27:34 +0000 (+0200) Subject: BUG/MAJOR: external-checks: use asynchronous signal delivery X-Git-Tag: v1.7-dev4~59 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=48d6bf2e820f34f26668a2425babe0da422c6dc3;p=thirdparty%2Fhaproxy.git BUG/MAJOR: external-checks: use asynchronous signal delivery There are random segfaults occuring when using external checks. The reason is that when receiving a SIGCHLD, a call to task_wakeup() is performed. There are two situations where this causes trouble : - the scheduler is in process_running_tasks(), since task_wakeup() sets rq_next to NULL, when the former dereferences it to get the next pointer, the program crashes ; - when another task_wakeup() is being called and during eb_next() in process_running_tasks(), because the structure of the run queue tree changes while it is being processed. The solution against this is to use asynchronous signal processing thanks to the internal signal API. The signal handler is registered, and upon delivery, the signal is added to the queue and processed out of any other processing. This code was stressed at 2500 forks/s and their respective signals for quite some time and the segfault is now gone. --- diff --git a/src/checks.c b/src/checks.c index 60f12264d9..ee0295edf3 100644 --- a/src/checks.c +++ b/src/checks.c @@ -58,6 +58,7 @@ #include #include #include +#include #include #include #include @@ -1603,25 +1604,22 @@ static void pid_list_expire(pid_t pid, int status) } } -static void sigchld_handler(int signal) +static void sigchld_handler(struct sig_handler *sh) { pid_t pid; int status; + while ((pid = waitpid(0, &status, WNOHANG)) > 0) pid_list_expire(pid, status); } -static int init_pid_list(void) { - struct sigaction action = { - .sa_handler = sigchld_handler, - .sa_flags = SA_NOCLDSTOP - }; - +static int init_pid_list(void) +{ if (pool2_pid_list != NULL) /* Nothing to do */ return 0; - if (sigaction(SIGCHLD, &action, NULL)) { + if (!signal_register_fct(SIGCHLD, sigchld_handler, SIGCHLD)) { Alert("Failed to set signal handler for external health checks: %s. Aborting.\n", strerror(errno)); return 1;