fusefs: fix another semi-infinite loop bug regarding signal handling

fticket_wait_answer would spin if it received an unhandled signal whose
default disposition is to terminate.  The reason is because msleep(9) would
return EINTR even for a masked signal.  One reason is when the thread is
stopped, which happens for example during sigexit().  Fix this bug by
returning immediately if fticket_wait_answer ever gets interrupted a second
time, for any reason.

Sponsored by:	The FreeBSD Foundation
This commit is contained in:
Alan Somers 2019-07-18 15:30:00 +00:00
parent d26d63a4af
commit f05962453e
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/projects/fuse2/; revision=350113

View File

@ -439,11 +439,12 @@ fticket_wait_answer(struct fuse_ticket *ftick)
sigset_t blockedset, oldset; sigset_t blockedset, oldset;
int err = 0, stops_deferred; int err = 0, stops_deferred;
struct fuse_data *data; struct fuse_data *data;
bool interrupted = false;
if (fsess_isimpl(ftick->tk_data->mp, FUSE_INTERRUPT)) { if (fsess_isimpl(ftick->tk_data->mp, FUSE_INTERRUPT)) {
SIGEMPTYSET(blockedset); SIGEMPTYSET(blockedset);
} else { } else {
/* May as well block all signals */ /* Block all signals except (implicitly) SIGKILL */
SIGFILLSET(blockedset); SIGFILLSET(blockedset);
} }
stops_deferred = sigdeferstop(SIGDEFERSTOP_SILENT); stops_deferred = sigdeferstop(SIGDEFERSTOP_SILENT);
@ -489,7 +490,6 @@ fticket_wait_answer(struct fuse_ticket *ftick)
* or EAGAIN to the interrupt. * or EAGAIN to the interrupt.
*/ */
sigset_t tmpset; sigset_t tmpset;
int sig;
SDT_PROBE2(fusefs, , ipc, trace, 4, SDT_PROBE2(fusefs, , ipc, trace, 4,
"fticket_wait_answer: interrupt"); "fticket_wait_answer: interrupt");
@ -498,22 +498,28 @@ fticket_wait_answer(struct fuse_ticket *ftick)
PROC_LOCK(td->td_proc); PROC_LOCK(td->td_proc);
mtx_lock(&td->td_proc->p_sigacts->ps_mtx); mtx_lock(&td->td_proc->p_sigacts->ps_mtx);
sig = cursig(td);
tmpset = td->td_proc->p_siglist; tmpset = td->td_proc->p_siglist;
SIGSETOR(tmpset, td->td_siglist); SIGSETOR(tmpset, td->td_siglist);
mtx_unlock(&td->td_proc->p_sigacts->ps_mtx); mtx_unlock(&td->td_proc->p_sigacts->ps_mtx);
PROC_UNLOCK(td->td_proc); PROC_UNLOCK(td->td_proc);
fuse_lck_mtx_lock(ftick->tk_aw_mtx); fuse_lck_mtx_lock(ftick->tk_aw_mtx);
if (!SIGISMEMBER(tmpset, SIGKILL)) { if (!interrupted && !SIGISMEMBER(tmpset, SIGKILL)) {
/* /*
* Block the just-delivered signal while we wait for an * Block all signals while we wait for an interrupt
* interrupt response * response. The protocol doesn't discriminate between
* different signals.
*/ */
SIGADDSET(blockedset, sig); SIGFILLSET(blockedset);
interrupted = true;
goto retry; goto retry;
} else { } else {
/* Return immediately for fatal signals */ /*
* Return immediately for fatal signals, or if this is
* the second interruption. We should only be
* interrupted twice if the thread is stopped, for
* example during sigexit.
*/
} }
} else if (err) { } else if (err) {
SDT_PROBE2(fusefs, , ipc, trace, 6, SDT_PROBE2(fusefs, , ipc, trace, 6,