Changeset View
Changeset View
Standalone View
Standalone View
sys/kern/kern_sig.c
Show First 20 Lines • Show All 151 Lines • ▼ Show 20 Lines | |||||
static int signal_alloc_fail = 0; | static int signal_alloc_fail = 0; | ||||
SYSCTL_INT(_kern_sigqueue, OID_AUTO, alloc_fail, CTLFLAG_RD, | SYSCTL_INT(_kern_sigqueue, OID_AUTO, alloc_fail, CTLFLAG_RD, | ||||
&signal_alloc_fail, 0, "signals failed to be allocated"); | &signal_alloc_fail, 0, "signals failed to be allocated"); | ||||
static int kern_lognosys = 0; | static int kern_lognosys = 0; | ||||
SYSCTL_INT(_kern, OID_AUTO, lognosys, CTLFLAG_RWTUN, &kern_lognosys, 0, | SYSCTL_INT(_kern, OID_AUTO, lognosys, CTLFLAG_RWTUN, &kern_lognosys, 0, | ||||
"Log invalid syscalls"); | "Log invalid syscalls"); | ||||
int fastblock_fetch_always = 0; | |||||
jeff: bool would be better.
Is this just for debugging? | |||||
kibAuthorUnsubmitted Done Inline Actionsok, I will change to bool. It is for field diagnostic, I want to be able to ask users to flip the switch back without providing the patch, if I suspect an issue with spurious EINTR/ERESTART. kib: ok, I will change to bool.
It is for field diagnostic, I want to be able to ask users to flip… | |||||
SYSCTL_INT(_kern, OID_AUTO, sigfastblock_fetch_always, CTLFLAG_RWTUN, | |||||
&fastblock_fetch_always, 0, | |||||
"Fetch sigfastblock word on each syscall entry for proper " | |||||
"blocking semantic"); | |||||
SYSINIT(signal, SI_SUB_P1003_1B, SI_ORDER_FIRST+3, sigqueue_start, NULL); | SYSINIT(signal, SI_SUB_P1003_1B, SI_ORDER_FIRST+3, sigqueue_start, NULL); | ||||
/* | /* | ||||
* Policy -- Can ucred cr1 send SIGIO to process cr2? | * Policy -- Can ucred cr1 send SIGIO to process cr2? | ||||
* Should use cr_cansignal() once cr_cansignal() allows SIGIO and SIGURG | * Should use cr_cansignal() once cr_cansignal() allows SIGIO and SIGURG | ||||
* in the right situations. | * in the right situations. | ||||
*/ | */ | ||||
#define CANSIGIO(cr1, cr2) \ | #define CANSIGIO(cr1, cr2) \ | ||||
▲ Show 20 Lines • Show All 1,832 Lines • ▼ Show 20 Lines | trapsignal(struct thread *td, ksiginfo_t *ksi) | ||||
sigset_t sigmask; | sigset_t sigmask; | ||||
int code, sig; | int code, sig; | ||||
p = td->td_proc; | p = td->td_proc; | ||||
sig = ksi->ksi_signo; | sig = ksi->ksi_signo; | ||||
code = ksi->ksi_code; | code = ksi->ksi_code; | ||||
KASSERT(_SIG_VALID(sig), ("invalid signal")); | KASSERT(_SIG_VALID(sig), ("invalid signal")); | ||||
sigfastblock_fetch(td); | |||||
PROC_LOCK(p); | PROC_LOCK(p); | ||||
ps = p->p_sigacts; | ps = p->p_sigacts; | ||||
mtx_lock(&ps->ps_mtx); | mtx_lock(&ps->ps_mtx); | ||||
sigmask = td->td_sigmask; | sigmask = td->td_sigmask; | ||||
if (td->td_sigblock_val != 0) | if (td->td_sigblock_val != 0) | ||||
SIGSETOR(sigmask, fastblock_mask); | SIGSETOR(sigmask, fastblock_mask); | ||||
if ((p->p_flag & P_TRACED) == 0 && SIGISMEMBER(ps->ps_sigcatch, sig) && | if ((p->p_flag & P_TRACED) == 0 && SIGISMEMBER(ps->ps_sigcatch, sig) && | ||||
!SIGISMEMBER(sigmask, sig)) { | !SIGISMEMBER(sigmask, sig)) { | ||||
▲ Show 20 Lines • Show All 1,931 Lines • ▼ Show 20 Lines | sig_drop_caught(struct proc *p) | ||||
while (SIGNOTEMPTY(ps->ps_sigcatch)) { | while (SIGNOTEMPTY(ps->ps_sigcatch)) { | ||||
sig = sig_ffs(&ps->ps_sigcatch); | sig = sig_ffs(&ps->ps_sigcatch); | ||||
sigdflt(ps, sig); | sigdflt(ps, sig); | ||||
if ((sigprop(sig) & SIGPROP_IGNORE) != 0) | if ((sigprop(sig) & SIGPROP_IGNORE) != 0) | ||||
sigqueue_delete_proc(p, sig); | sigqueue_delete_proc(p, sig); | ||||
} | } | ||||
} | } | ||||
static void | |||||
sigfastblock_failed(struct thread *td, bool sendsig, bool write) | |||||
{ | |||||
ksiginfo_t ksi; | |||||
/* | |||||
* Prevent further fetches and SIGSEGVs, allowing thread to | |||||
* issue syscalls despite corruption. | |||||
*/ | |||||
sigfastblock_clear(td); | |||||
if (!sendsig) | |||||
return; | |||||
ksiginfo_init_trap(&ksi); | |||||
ksi.ksi_signo = SIGSEGV; | |||||
ksi.ksi_code = write ? SEGV_ACCERR : SEGV_MAPERR; | |||||
ksi.ksi_addr = td->td_sigblock_ptr; | |||||
trapsignal(td, &ksi); | |||||
} | |||||
static bool | |||||
sigfastblock_fetch_sig(struct thread *td, bool sendsig, uint32_t *valp) | |||||
{ | |||||
uint32_t res; | |||||
if ((td->td_pflags & TDP_SIGFASTBLOCK) == 0) | |||||
return (true); | |||||
if (fueword32((void *)td->td_sigblock_ptr, &res) == -1) { | |||||
sigfastblock_failed(td, sendsig, false); | |||||
return (false); | |||||
} | |||||
*valp = res; | |||||
td->td_sigblock_val = res & ~SIGFASTBLOCK_FLAGS; | |||||
return (true); | |||||
} | |||||
int | int | ||||
sys_sigfastblock(struct thread *td, struct sigfastblock_args *uap) | sys_sigfastblock(struct thread *td, struct sigfastblock_args *uap) | ||||
{ | { | ||||
struct proc *p; | struct proc *p; | ||||
int error, res; | int error, res; | ||||
uint32_t oldval; | uint32_t oldval; | ||||
error = 0; | error = 0; | ||||
p = td->td_proc; | |||||
switch (uap->cmd) { | switch (uap->cmd) { | ||||
case SIGFASTBLOCK_SETPTR: | case SIGFASTBLOCK_SETPTR: | ||||
if ((td->td_pflags & TDP_SIGFASTBLOCK) != 0) { | if ((td->td_pflags & TDP_SIGFASTBLOCK) != 0) { | ||||
error = EBUSY; | error = EBUSY; | ||||
break; | break; | ||||
} | } | ||||
if (((uintptr_t)(uap->ptr) & (sizeof(uint32_t) - 1)) != 0) { | if (((uintptr_t)(uap->ptr) & (sizeof(uint32_t) - 1)) != 0) { | ||||
error = EINVAL; | error = EINVAL; | ||||
break; | break; | ||||
} | } | ||||
td->td_pflags |= TDP_SIGFASTBLOCK; | td->td_pflags |= TDP_SIGFASTBLOCK; | ||||
td->td_sigblock_ptr = uap->ptr; | td->td_sigblock_ptr = uap->ptr; | ||||
break; | break; | ||||
case SIGFASTBLOCK_UNBLOCK: | case SIGFASTBLOCK_UNBLOCK: | ||||
if ((td->td_pflags & TDP_SIGFASTBLOCK) != 0) { | if ((td->td_pflags & TDP_SIGFASTBLOCK) == 0) { | ||||
error = EINVAL; | error = EINVAL; | ||||
break; | break; | ||||
} | } | ||||
again: | |||||
res = casueword32(td->td_sigblock_ptr, SIGFASTBLOCK_PEND, | for (;;) { | ||||
&oldval, 0); | res = casueword32(td->td_sigblock_ptr, | ||||
SIGFASTBLOCK_PEND, &oldval, 0); | |||||
if (res == -1) { | if (res == -1) { | ||||
error = EFAULT; | error = EFAULT; | ||||
sigfastblock_failed(td, false, true); | |||||
break; | break; | ||||
} | } | ||||
if (res == 1) { | if (res == 0) | ||||
break; | |||||
MPASS(res == 1); | |||||
if (oldval != SIGFASTBLOCK_PEND) { | if (oldval != SIGFASTBLOCK_PEND) { | ||||
error = EBUSY; | error = EBUSY; | ||||
break; | break; | ||||
} | } | ||||
error = thread_check_susp(td, false); | error = thread_check_susp(td, false); | ||||
if (error != 0) | if (error != 0) | ||||
break; | break; | ||||
goto again; | |||||
} | } | ||||
if (error != 0) | |||||
break; | |||||
/* | |||||
* td_sigblock_val is cleared there, but not on a | |||||
* syscall exit. The end effect is that a single | |||||
* interruptible sleep, while user sigblock word is | |||||
* set, might return EINTR or ERESTART to usermode | |||||
* without delivering signal. All further sleeps, | |||||
* until userspace clears the word and does | |||||
* sigfastblock(UNBLOCK), observe current word and no | |||||
* longer get interrupted. It is slight | |||||
* non-conformance, with alternative to have read the | |||||
* sigblock word on each syscall entry. | |||||
*/ | |||||
td->td_sigblock_val = 0; | td->td_sigblock_val = 0; | ||||
/* | /* | ||||
* Rely on normal ast mechanism to deliver pending | * Rely on normal ast mechanism to deliver pending | ||||
* signals to current thread. But notify others about | * signals to current thread. But notify others about | ||||
* fake unblock. | * fake unblock. | ||||
*/ | */ | ||||
p = td->td_proc; | |||||
if (error == 0 && p->p_numthreads != 1) { | if (error == 0 && p->p_numthreads != 1) { | ||||
PROC_LOCK(p); | PROC_LOCK(p); | ||||
reschedule_signals(p, td->td_sigmask, 0); | reschedule_signals(p, td->td_sigmask, 0); | ||||
PROC_UNLOCK(p); | PROC_UNLOCK(p); | ||||
} | } | ||||
break; | break; | ||||
case SIGFASTBLOCK_UNSETPTR: | case SIGFASTBLOCK_UNSETPTR: | ||||
if ((td->td_pflags & TDP_SIGFASTBLOCK) == 0) { | if ((td->td_pflags & TDP_SIGFASTBLOCK) == 0) { | ||||
error = EINVAL; | error = EINVAL; | ||||
break; | break; | ||||
} | } | ||||
res = fueword32(td->td_sigblock_ptr, &oldval); | if (!sigfastblock_fetch_sig(td, false, &oldval)) { | ||||
if (res == -1) { | |||||
error = EFAULT; | error = EFAULT; | ||||
break; | break; | ||||
} | } | ||||
if (oldval != 0 && oldval != SIGFASTBLOCK_PEND) { | if (oldval != 0 && oldval != SIGFASTBLOCK_PEND) { | ||||
error = EBUSY; | error = EBUSY; | ||||
break; | break; | ||||
} | } | ||||
td->td_pflags &= ~TDP_SIGFASTBLOCK; | sigfastblock_clear(td); | ||||
td->td_sigblock_val = 0; | |||||
break; | break; | ||||
default: | default: | ||||
error = EINVAL; | error = EINVAL; | ||||
break; | break; | ||||
} | } | ||||
return (error); | return (error); | ||||
} | } | ||||
void | void | ||||
fetch_sigfastblock(struct thread *td) | sigfastblock_clear(struct thread *td) | ||||
{ | { | ||||
struct proc *p; | |||||
bool resched; | |||||
if ((td->td_pflags & TDP_SIGFASTBLOCK) == 0) | if ((td->td_pflags & TDP_SIGFASTBLOCK) == 0) | ||||
return; | return; | ||||
if (fueword32(td->td_sigblock_ptr, &td->td_sigblock_val) == -1) { | td->td_sigblock_val = 0; | ||||
fetch_sigfastblock_failed(td, false); | resched = (td->td_pflags & TDP_SIGFASTPENDING) != 0; | ||||
return; | td->td_pflags &= ~(TDP_SIGFASTBLOCK | TDP_SIGFASTPENDING); | ||||
if (resched) { | |||||
p = td->td_proc; | |||||
PROC_LOCK(p); | |||||
reschedule_signals(p, td->td_sigmask, 0); | |||||
PROC_UNLOCK(p); | |||||
} | } | ||||
td->td_sigblock_val &= ~SIGFASTBLOCK_FLAGS; | |||||
} | } | ||||
void | void | ||||
fetch_sigfastblock_failed(struct thread *td, bool write) | sigfastblock_fetch(struct thread *td) | ||||
{ | { | ||||
ksiginfo_t ksi; | uint32_t val; | ||||
/* | (void)sigfastblock_fetch_sig(td, true, &val); | ||||
* Prevent further fetches and SIGSEGVs, allowing thread to | } | ||||
* issue syscalls despite corruption. | |||||
*/ | |||||
td->td_pflags &= ~TDP_SIGFASTBLOCK; | |||||
ksiginfo_init_trap(&ksi); | void | ||||
ksi.ksi_signo = SIGSEGV; | sigfastblock_setpend(struct thread *td) | ||||
ksi.ksi_code = write ? SEGV_ACCERR : SEGV_MAPERR; | { | ||||
ksi.ksi_addr = td->td_sigblock_ptr; | int res; | ||||
trapsignal(td, &ksi); | uint32_t oldval; | ||||
if ((td->td_pflags & TDP_SIGFASTBLOCK) == 0) | |||||
return; | |||||
res = fueword32((void *)td->td_sigblock_ptr, &oldval); | |||||
if (res == -1) { | |||||
sigfastblock_failed(td, true, false); | |||||
return; | |||||
} | |||||
for (;;) { | |||||
res = casueword32(td->td_sigblock_ptr, oldval, &oldval, | |||||
oldval | SIGFASTBLOCK_PEND); | |||||
if (res == -1) { | |||||
sigfastblock_failed(td, true, true); | |||||
return; | |||||
} | |||||
if (res == 0) { | |||||
td->td_sigblock_val = oldval & ~SIGFASTBLOCK_FLAGS; | |||||
td->td_pflags &= ~TDP_SIGFASTPENDING; | |||||
break; | |||||
} | |||||
MPASS(res == 1); | |||||
if (thread_check_susp(td, false) != 0) | |||||
break; | |||||
} | |||||
} | } |
bool would be better.
Is this just for debugging?