rc: block SIGCHLD during pid list operations

the pid list will be accessed inside the SIGCHLD signal handler. so we
must ensure SIGCHLD handler doesn't get invoked while the list is at an
inconsistent state making it unsafe to interact with.

Co-authored-by: Dominique MARTINET <dominique.martinet@atmark-techno.com>
Bug: https://github.com/OpenRC/openrc/issues/589#issuecomment-1406588576
This commit is contained in:
NRK 2023-01-28 18:45:13 +06:00 committed by William Hubbs
parent bbd3acfc67
commit 0b4732520f

View File

@ -351,16 +351,31 @@ static char *get_krunlevel(void)
static void static void
add_pid(pid_t pid) add_pid(pid_t pid)
{ {
sigset_t sset, old;
RC_PID *p = xmalloc(sizeof(*p)); RC_PID *p = xmalloc(sizeof(*p));
p->pid = pid; p->pid = pid;
/* this list will be accessed inside the SIGCHLD signal handler.
* so we need to ensure that the SIGCHLD handler doesn't get invoked
* while the list is at an inconsistent state.
*/
sigemptyset(&sset);
sigaddset(&sset, SIGCHLD);
sigprocmask(SIG_SETMASK, &sset, &old);
LIST_INSERT_HEAD(&service_pids, p, entries); LIST_INSERT_HEAD(&service_pids, p, entries);
sigprocmask(SIG_SETMASK, &old, NULL);
} }
static void static void
remove_pid(pid_t pid, bool inside_signal) remove_pid(pid_t pid, bool inside_signal)
{ {
sigset_t sset, old;
RC_PID *p, *tmp; RC_PID *p, *tmp;
/* same rationale for blocking SIGCHLD as add_pid() */
sigemptyset(&sset);
sigaddset(&sset, SIGCHLD);
sigprocmask(SIG_SETMASK, &sset, &old);
LIST_FOREACH(p, &service_pids, entries) { LIST_FOREACH(p, &service_pids, entries) {
if (p->pid == pid) { if (p->pid == pid) {
LIST_REMOVE(p, entries); LIST_REMOVE(p, entries);
@ -375,6 +390,7 @@ remove_pid(pid_t pid, bool inside_signal)
free(p); free(p);
} }
} }
sigprocmask(SIG_SETMASK, &old, NULL);
} }
static void static void