diff --git a/sys/kern/kern_sig.c b/sys/kern/kern_sig.c --- a/sys/kern/kern_sig.c +++ b/sys/kern/kern_sig.c @@ -365,6 +365,16 @@ SIGFILLSET(fastblock_mask); SIG_CANTMASK(fastblock_mask); ast_register(TDA_SIG, ASTR_UNCOND, 0, ast_sig); + + /* + * TDA_SIGSUSPEND_EARLY is for the case where the signal mask should be + * restored before delivering any signals so that we do not deliver any + * that are blocked by the normal thread mask. It is mutually exclusive + * with TDA_SIGSUSPEND, which should be used if we *do want to deliver + * signals that are normally blocked, e.g., if it interrupted our sleep. + */ + ast_register(TDA_SIGSUSPEND_EARLY, ASTR_ASTF_REQUIRED | ASTR_TDP, + TDP_OLDMASK, ast_sigsuspend); ast_register(TDA_SIGSUSPEND, ASTR_ASTF_REQUIRED | ASTR_TDP, TDP_OLDMASK, ast_sigsuspend); } diff --git a/sys/kern/sys_generic.c b/sys/kern/sys_generic.c --- a/sys/kern/sys_generic.c +++ b/sys/kern/sys_generic.c @@ -1049,14 +1049,26 @@ if (error != 0) return (error); td->td_pflags |= TDP_OLDMASK; + } + error = kern_select(td, nd, in, ou, ex, tvp, abi_nfdbits); + if (uset != NULL) { /* * Make sure that ast() is called on return to * usermode and TDP_OLDMASK is cleared, restoring old - * sigmask. + * sigmask. If we didn't get interrupted, then the caller is + * likely not expecting a signal to hit that should normally be + * blocked by its signal mask, so we restore the mask before + * any signals could be delivered. */ - ast_sched(td, TDA_SIGSUSPEND); + if (error == EINTR) { + ast_sched(td, TDA_SIGSUSPEND); + } else { + /* *select(2) should never restart. */ + MPASS(error != ERESTART); + ast_sched(td, TDA_SIGSUSPEND_EARLY); + } } - error = kern_select(td, nd, in, ou, ex, tvp, abi_nfdbits); + return (error); } @@ -1528,12 +1540,6 @@ if (error) return (error); td->td_pflags |= TDP_OLDMASK; - /* - * Make sure that ast() is called on return to - * usermode and TDP_OLDMASK is cleared, restoring old - * sigmask. - */ - ast_sched(td, TDA_SIGSUSPEND); } seltdinit(td); @@ -1556,6 +1562,22 @@ error = EINTR; if (error == EWOULDBLOCK) error = 0; + + if (uset != NULL) { + /* + * Make sure that ast() is called on return to + * usermode and TDP_OLDMASK is cleared, restoring old + * sigmask. If we didn't get interrupted, then the caller is + * likely not expecting a signal to hit that should normally be + * blocked by its signal mask, so we restore the mask before + * any signals could be delivered. + */ + if (error == EINTR) + ast_sched(td, TDA_SIGSUSPEND); + else + ast_sched(td, TDA_SIGSUSPEND_EARLY); + } + return (error); } diff --git a/sys/sys/proc.h b/sys/sys/proc.h --- a/sys/sys/proc.h +++ b/sys/sys/proc.h @@ -494,6 +494,7 @@ TDA_RACCT, TDA_MOD1, /* For third party use, before signals are */ TAD_MOD2, /* processed .. */ + TDA_SIGSUSPEND_EARLY, /* For discarding temporary signal mask */ TDA_SIG, TDA_KTRACE, TDA_SUSPEND,