su: Fix never alarmed SIGKILL when session terminates
The buggy code was introduced nearly 5 years ago at the commit 08fd4b69e84364677a10e519ccb25b71710ee686. The desired behavior is that SIGKILL will be sent to the child if it does not exit within 2 seconds after it receives SIGTERM. However, SIGALRM is masked while waiting for the child so it cannot wake the program up after 2 seconds to send SIGKILL. An example shows the buggy behavior, which exists in Ubuntu 18.04 LTS (with login 1:4.5-1ubuntu2). ```bash user1@localhost:~$ su user2 -c ' _term() { echo SIGTERM received } trap _term TERM while true; do sleep 1 echo still alive done' Password: still alive Session terminated, terminating shell...Terminated SIGTERM received still alive still alive still alive still alive ``` (SIGTERM is sent in another user1's terminal by executing `killall su`.) Here is the desired behavior, which shows what the commit fixes. ```bash user1@localhost:~$ su user2 -c ' _term() { echo SIGTERM received } trap _term TERM while true; do sleep 1 echo still alive done' Password: still alive Session terminated, terminating shell...Terminated SIGTERM received still alive still alive ...killed. user1@localhost:~$ echo $? 255 ```
This commit is contained in:
parent
cdc8c1e25b
commit
5b4082d007
28
src/su.c
28
src/su.c
@ -397,22 +397,28 @@ static void prepare_pam_close_session (void)
|
|||||||
snprintf (kill_msg, sizeof kill_msg, _(" ...killed.\n"));
|
snprintf (kill_msg, sizeof kill_msg, _(" ...killed.\n"));
|
||||||
snprintf (wait_msg, sizeof wait_msg, _(" ...waiting for child to terminate.\n"));
|
snprintf (wait_msg, sizeof wait_msg, _(" ...waiting for child to terminate.\n"));
|
||||||
|
|
||||||
|
/* Any signals other than SIGCHLD and SIGALRM will no longer have any effect,
|
||||||
|
* so it's time to block all of them. */
|
||||||
|
sigfillset (&ourset);
|
||||||
|
if (sigprocmask (SIG_BLOCK, &ourset, NULL) != 0) {
|
||||||
|
fprintf (stderr, _("%s: signal masking malfunction\n"), Prog);
|
||||||
|
kill_child (0);
|
||||||
|
/* Never reach (_exit called). */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Send SIGKILL to the child if it doesn't
|
||||||
|
* exit within 2 seconds (after SIGTERM) */
|
||||||
(void) signal (SIGALRM, kill_child);
|
(void) signal (SIGALRM, kill_child);
|
||||||
(void) signal (SIGCHLD, catch_signals);
|
(void) signal (SIGCHLD, catch_signals);
|
||||||
(void) alarm (2);
|
(void) alarm (2);
|
||||||
|
|
||||||
sigemptyset (&ourset);
|
(void) sigdelset (&ourset, SIGALRM);
|
||||||
if ((sigaddset (&ourset, SIGALRM) != 0)
|
(void) sigdelset (&ourset, SIGCHLD);
|
||||||
|| (sigprocmask (SIG_BLOCK, &ourset, NULL) != 0)) {
|
|
||||||
fprintf (stderr, _("%s: signal masking malfunction\n"), Prog);
|
while (0 == waitpid (pid_child, &status, WNOHANG)) {
|
||||||
kill_child (0);
|
sigsuspend (&ourset);
|
||||||
} else {
|
|
||||||
while (0 == waitpid (pid_child, &status, WNOHANG)) {
|
|
||||||
sigsuspend (&ourset);
|
|
||||||
}
|
|
||||||
pid_child = 0;
|
|
||||||
(void) sigprocmask (SIG_UNBLOCK, &ourset, NULL);
|
|
||||||
}
|
}
|
||||||
|
pid_child = 0;
|
||||||
|
|
||||||
(void) fputs (_(" ...terminated.\n"), stderr);
|
(void) fputs (_(" ...terminated.\n"), stderr);
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user