Page MenuHomeFreeBSD

D23622.id68414.diff
No OneTemporary

D23622.id68414.diff

Index: sys/kern/kern_exec.c
===================================================================
--- sys/kern/kern_exec.c
+++ sys/kern/kern_exec.c
@@ -1035,9 +1035,7 @@
imgp->vmspace_destroyed = 1;
imgp->sysent = sv;
- td->td_pflags &= ~TDP_SIGFASTBLOCK;
- td->td_sigblock_ptr = NULL;
- td->td_sigblock_val = 0;
+ sigfastblock_clear(td);
/* May be called with Giant held */
EVENTHANDLER_DIRECT_INVOKE(process_exec, p, imgp);
Index: sys/kern/kern_sig.c
===================================================================
--- sys/kern/kern_sig.c
+++ sys/kern/kern_sig.c
@@ -157,6 +157,12 @@
SYSCTL_INT(_kern, OID_AUTO, lognosys, CTLFLAG_RWTUN, &kern_lognosys, 0,
"Log invalid syscalls");
+__read_frequently bool sigfastblock_fetch_always = false;
+SYSCTL_BOOL(_kern, OID_AUTO, sigfastblock_fetch_always, CTLFLAG_RWTUN,
+ &sigfastblock_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);
/*
@@ -2005,6 +2011,7 @@
code = ksi->ksi_code;
KASSERT(_SIG_VALID(sig), ("invalid signal"));
+ sigfastblock_fetch(td);
PROC_LOCK(p);
ps = p->p_sigacts;
mtx_lock(&ps->ps_mtx);
@@ -3952,6 +3959,42 @@
}
}
+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
sys_sigfastblock(struct thread *td, struct sigfastblock_args *uap)
{
@@ -3960,6 +4003,7 @@
uint32_t oldval;
error = 0;
+ p = td->td_proc;
switch (uap->cmd) {
case SIGFASTBLOCK_SETPTR:
if ((td->td_pflags & TDP_SIGFASTBLOCK) != 0) {
@@ -3975,18 +4019,22 @@
break;
case SIGFASTBLOCK_UNBLOCK:
- if ((td->td_pflags & TDP_SIGFASTBLOCK) != 0) {
+ if ((td->td_pflags & TDP_SIGFASTBLOCK) == 0) {
error = EINVAL;
break;
}
-again:
- res = casueword32(td->td_sigblock_ptr, SIGFASTBLOCK_PEND,
- &oldval, 0);
- if (res == -1) {
- error = EFAULT;
- break;
- }
- if (res == 1) {
+
+ for (;;) {
+ res = casueword32(td->td_sigblock_ptr,
+ SIGFASTBLOCK_PEND, &oldval, 0);
+ if (res == -1) {
+ error = EFAULT;
+ sigfastblock_failed(td, false, true);
+ break;
+ }
+ if (res == 0)
+ break;
+ MPASS(res == 1);
if (oldval != SIGFASTBLOCK_PEND) {
error = EBUSY;
break;
@@ -3994,8 +4042,22 @@
error = thread_check_susp(td, false);
if (error != 0)
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;
/*
@@ -4003,7 +4065,6 @@
* signals to current thread. But notify others about
* fake unblock.
*/
- p = td->td_proc;
if (error == 0 && p->p_numthreads != 1) {
PROC_LOCK(p);
reschedule_signals(p, td->td_sigmask, 0);
@@ -4016,8 +4077,7 @@
error = EINVAL;
break;
}
- res = fueword32(td->td_sigblock_ptr, &oldval);
- if (res == -1) {
+ if (!sigfastblock_fetch_sig(td, false, &oldval)) {
error = EFAULT;
break;
}
@@ -4025,8 +4085,7 @@
error = EBUSY;
break;
}
- td->td_pflags &= ~TDP_SIGFASTBLOCK;
- td->td_sigblock_val = 0;
+ sigfastblock_clear(td);
break;
default:
@@ -4037,32 +4096,59 @@
}
void
-fetch_sigfastblock(struct thread *td)
+sigfastblock_clear(struct thread *td)
{
+ struct proc *p;
+ bool resched;
if ((td->td_pflags & TDP_SIGFASTBLOCK) == 0)
return;
- if (fueword32(td->td_sigblock_ptr, &td->td_sigblock_val) == -1) {
- fetch_sigfastblock_failed(td, false);
- return;
+ td->td_sigblock_val = 0;
+ resched = (td->td_pflags & TDP_SIGFASTPENDING) != 0;
+ 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
-fetch_sigfastblock_failed(struct thread *td, bool write)
+sigfastblock_fetch(struct thread *td)
{
- ksiginfo_t ksi;
+ uint32_t val;
- /*
- * Prevent further fetches and SIGSEGVs, allowing thread to
- * issue syscalls despite corruption.
- */
- td->td_pflags &= ~TDP_SIGFASTBLOCK;
+ (void)sigfastblock_fetch_sig(td, true, &val);
+}
- 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);
+void
+sigfastblock_setpend(struct thread *td)
+{
+ int res;
+ 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;
+ }
}
Index: sys/kern/subr_syscall.c
===================================================================
--- sys/kern/subr_syscall.c
+++ sys/kern/subr_syscall.c
@@ -132,11 +132,11 @@
}
/*
- * Fetch fast sigblock value at the time of syscall
- * entry because sleepqueue primitives might call
- * cursig().
+ * Fetch fast sigblock value at the time of syscall entry to
+ * handle sleepqueue primitives which might call cursig().
*/
- fetch_sigfastblock(td);
+ if (__predict_false(sigfastblock_fetch_always))
+ (void)sigfastblock_fetch(td);
/* Let system calls set td_errno directly. */
td->td_pflags &= ~TDP_NERRNO;
Index: sys/kern/subr_trap.c
===================================================================
--- sys/kern/subr_trap.c
+++ sys/kern/subr_trap.c
@@ -134,6 +134,7 @@
#ifdef KTRACE
KTRUSERRET(td);
#endif
+
td_softdep_cleanup(td);
MPASS(td->td_su == NULL);
@@ -222,8 +223,7 @@
{
struct thread *td;
struct proc *p;
- uint32_t oldval;
- int flags, sig, res;
+ int flags, sig;
td = curthread;
p = td->td_proc;
@@ -325,7 +325,7 @@
*/
if (flags & TDF_NEEDSIGCHK || p->p_pendingcnt > 0 ||
!SIGISEMPTY(p->p_siglist)) {
- fetch_sigfastblock(td);
+ sigfastblock_fetch(td);
PROC_LOCK(p);
mtx_lock(&p->p_sigacts->ps_mtx);
if ((td->td_pflags & TDP_SIGFASTBLOCK) != 0 &&
@@ -346,32 +346,8 @@
* Handle deferred update of the fast sigblock value, after
* the postsig() loop was performed.
*/
- if (td->td_pflags & TDP_SIGFASTPENDING) {
- td->td_pflags &= ~TDP_SIGFASTPENDING;
- res = fueword32(td->td_sigblock_ptr, &oldval);
- if (res == -1) {
- fetch_sigfastblock_failed(td, false);
- } else {
- for (;;) {
- oldval |= SIGFASTBLOCK_PEND;
- res = casueword32(td->td_sigblock_ptr, oldval,
- &oldval, oldval | SIGFASTBLOCK_PEND);
- if (res == -1) {
- fetch_sigfastblock_failed(td, true);
- break;
- }
- if (res == 0) {
- td->td_sigblock_val = oldval &
- ~SIGFASTBLOCK_FLAGS;
- break;
- }
- MPASS(res == 1);
- res = thread_check_susp(td, false);
- if (res != 0)
- break;
- }
- }
- }
+ if (td->td_pflags & TDP_SIGFASTPENDING)
+ sigfastblock_setpend(td);
/*
* We need to check to see if we have to exit or wait due to a
Index: sys/sys/signalvar.h
===================================================================
--- sys/sys/signalvar.h
+++ sys/sys/signalvar.h
@@ -273,6 +273,7 @@
#ifdef _KERNEL
extern sigset_t fastblock_mask;
+extern bool sigfastblock_fetch_always;
/* Return nonzero if process p has an unmasked pending signal. */
#define SIGPENDING(td) \
@@ -382,8 +383,6 @@
int cursig(struct thread *td);
void execsigs(struct proc *p);
-void fetch_sigfastblock(struct thread *td);
-void fetch_sigfastblock_failed(struct thread *td, bool write);
void gsignal(int pgid, int sig, ksiginfo_t *ksi);
void killproc(struct proc *p, char *why);
ksiginfo_t * ksiginfo_alloc(int wait);
@@ -405,6 +404,9 @@
void sigexit(struct thread *td, int sig) __dead2;
int sigev_findtd(struct proc *p, struct sigevent *sigev, struct thread **);
int sig_ffs(sigset_t *set);
+void sigfastblock_clear(struct thread *td);
+void sigfastblock_fetch(struct thread *td);
+void sigfastblock_setpend(struct thread *td);
void siginit(struct proc *p);
void signotify(struct thread *td);
void sigqueue_delete(struct sigqueue *queue, int sig);

File Metadata

Mime Type
text/plain
Expires
Wed, Oct 7, 3:51 PM (15 h, 9 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
40357729
Default Alt Text
D23622.id68414.diff (9 KB)

Event Timeline