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:
parent
d26d63a4af
commit
f05962453e
Notes:
svn2git
2020-12-20 02:59:44 +00:00
svn path=/projects/fuse2/; revision=350113
@ -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,
|
||||||
|
Loading…
Reference in New Issue
Block a user