On 07/07/14(Mon) 13:46, Matthew Dempsky wrote:
> p_sigmask is only modified by the owning thread from process context
> (e.g., sys_sigprocmask(), sys_sigreturn(), userret(), or postsig()),
> but it can be accessed anywhere (e.g., interrupts or threads on other
> CPUs). Currently sys_sigprocmask() protects p_sigmask with splhigh(),
> but that's not MP-safe.
>
>
> The simpler solution is to take advantage of our atomics APIs.
> Unfortunately for implementing SIG_SETMASK, we don't have an
> atomic_store_int() function, and I can't bring myself to abuse
> "volatile" for this purpose, so I've resorted to atomic_swap_uint().
>
> While here, I also refactored the P_SIGSUSPEND code a bit so there's
> less code duplication.
>
> I've included just amd64 and i386's machdep.c for now, because those
> were the only ones I'm readily able to test. The others should be
> easy to similarly fix though, and I can send a followup diff for them.
There was a previous attempt to mark sigpromask(2) as nolock but if I
recall properly setting p_sigmask atomically is not enough and there's
still a possible race in ptsignal().
Have you looked into this function, is it safe now?
> Index: sys/proc.h
> ===================================================================
> RCS file: /home/matthew/cvs-mirror/cvs/src/sys/sys/proc.h,v
> retrieving revision 1.186
> diff -u -p -r1.186 proc.h
> --- sys/proc.h 15 May 2014 03:52:25 -0000 1.186
> +++ sys/proc.h 7 Jul 2014 19:23:06 -0000
> @@ -520,6 +520,8 @@ int proc_cansugid(struct proc *);
> void proc_finish_wait(struct proc *, struct proc *);
> void process_zap(struct process *);
> void proc_free(struct proc *);
> +void proc_sigsetmask(struct proc *, sigset_t);
> +void proc_sigsuspend(struct proc *, sigset_t);
>
> struct sleep_state {
> int sls_s;
> @@ -559,4 +561,3 @@ struct cpu_info *cpuset_first(struct cpu
>
> #endif /* _KERNEL */
> #endif /* !_SYS_PROC_H_ */
> -
> Index: kern/kern_sig.c
> ===================================================================
> RCS file: /home/matthew/cvs-mirror/cvs/src/sys/kern/kern_sig.c,v
> retrieving revision 1.166
> diff -u -p -r1.166 kern_sig.c
> --- kern/kern_sig.c 4 May 2014 05:03:26 -0000 1.166
> +++ kern/kern_sig.c 7 Jul 2014 19:24:07 -0000
> @@ -62,6 +62,7 @@
> #include <sys/ptrace.h>
> #include <sys/sched.h>
> #include <sys/user.h>
> +#include <sys/atomic.h>
>
> #include <sys/mount.h>
> #include <sys/syscallargs.h>
> @@ -439,37 +440,55 @@ sys_sigprocmask(struct proc *p, void *v,
> syscallarg(int) how;
> syscallarg(sigset_t) mask;
> } */ *uap = v;
> - int error = 0;
> - int s;
> - sigset_t mask;
> + sigset_t mask = SCARG(uap, mask);
>
> *retval = p->p_sigmask;
> - mask = SCARG(uap, mask);
> - s = splhigh();
>
> switch (SCARG(uap, how)) {
> case SIG_BLOCK:
> - p->p_sigmask |= mask &~ sigcantmask;
> + atomic_setbits_int(&p->p_sigmask, mask &~ sigcantmask);
> break;
> case SIG_UNBLOCK:
> - p->p_sigmask &= ~mask;
> + atomic_clearbits_int(&p->p_sigmask, mask);
> break;
> case SIG_SETMASK:
> - p->p_sigmask = mask &~ sigcantmask;
> + proc_sigsetmask(p, mask &~ sigcantmask);
> break;
> default:
> - error = EINVAL;
> - break;
> + return (EINVAL);
> }
> - splx(s);
> - return (error);
> +
> + return (0);
> +}
> +
> +void
> +proc_sigsetmask(struct proc *p, sigset_t mask)
> +{
> + KASSERT(p == curproc);
> +
> + /* XXX: An atomic store would suffice here. */
> + (void)atomic_swap_uint(&p->p_sigmask, mask);
> +}
> +
> +/*
> + * Temporarily replace calling proc's signal mask for the duration of a
> + * system call. Original signal mask will be restored by userret().
> + */
> +void
> +proc_sigsuspend(struct proc *p, sigset_t mask)
> +{
> + KASSERT(p == curproc);
> + KASSERT(!(p->p_flag & P_SIGSUSPEND));
> +
> + p->p_oldmask = p->p_sigmask;
> + atomic_setbits_int(&p->p_flag, P_SIGSUSPEND);
> + proc_sigsetmask(p, mask);
> }
>
> /* ARGSUSED */
> int
> sys_sigpending(struct proc *p, void *v, register_t *retval)
> {
> -
> *retval = p->p_siglist;
> return (0);
> }
> @@ -489,16 +508,7 @@ sys_sigsuspend(struct proc *p, void *v,
> struct process *pr = p->p_p;
> struct sigacts *ps = pr->ps_sigacts;
>
> - /*
> - * When returning from sigpause, we want
> - * the old mask to be restored after the
> - * signal handler has finished. Thus, we
> - * save it here and mark the sigacts structure
> - * to indicate this.
> - */
> - p->p_oldmask = p->p_sigmask;
> - atomic_setbits_int(&p->p_flag, P_SIGSUSPEND);
> - p->p_sigmask = SCARG(uap, mask) &~ sigcantmask;
> + proc_sigsuspend(p, SCARG(uap, mask) &~ sigcantmask);
> while (tsleep(ps, PPAUSE|PCATCH, "pause", 0) == 0)
> /* void */;
> /* always return EINTR rather than ERESTART... */
> Index: kern/sys_generic.c
> ===================================================================
> RCS file: /home/matthew/cvs-mirror/cvs/src/sys/kern/sys_generic.c,v
> retrieving revision 1.86
> diff -u -p -r1.86 sys_generic.c
> --- kern/sys_generic.c 30 Mar 2014 21:54:48 -0000 1.86
> +++ kern/sys_generic.c 7 Jul 2014 19:24:07 -0000
> @@ -658,9 +620,7 @@ dopselect(struct proc *p, int nd, fd_set
> timo = 0;
>
> if (sigmask) {
> - p->p_oldmask = p->p_sigmask;
> - atomic_setbits_int(&p->p_flag, P_SIGSUSPEND);
> - p->p_sigmask = *sigmask &~ sigcantmask;
> + proc_sigsuspend(p, *sigmask &~ sigcantmask);
> }
>
> retry:
> @@ -966,9 +926,7 @@ doppoll(struct proc *p, struct pollfd *f
> timo = 0;
>
> if (sigmask) {
> - p->p_oldmask = p->p_sigmask;
> - atomic_setbits_int(&p->p_flag, P_SIGSUSPEND);
> - p->p_sigmask = *sigmask &~ sigcantmask;
> + proc_sigsuspend(p, *sigmask &~ sigcantmask);
> }
>
> retry:
> Index: compat/linux/linux_signal.c
> ===================================================================
> RCS file: /home/matthew/cvs-mirror/cvs/src/sys/compat/linux/linux_signal.c,v
> retrieving revision 1.17
> diff -u -p -r1.17 linux_signal.c
> --- compat/linux/linux_signal.c 26 Mar 2014 05:23:42 -0000 1.17
> +++ compat/linux/linux_signal.c 7 Jul 2014 19:24:07 -0000
> @@ -584,7 +584,6 @@ linux_sys_sigprocmask(p, v, retval)
> linux_old_sigset_t ss;
> sigset_t bs;
> int error = 0;
> - int s;
>
> *retval = 0;
>
> @@ -604,19 +603,17 @@ linux_sys_sigprocmask(p, v, retval)
>
> linux_old_to_bsd_sigset(&ss, &bs);
>
> - s = splhigh();
> -
> switch (SCARG(uap, how)) {
> case LINUX_SIG_BLOCK:
> - p->p_sigmask |= bs & ~sigcantmask;
> + atomic_setbits_int(&p->p_sigmask, bs &~ sigcantmask);
> break;
>
> case LINUX_SIG_UNBLOCK:
> - p->p_sigmask &= ~bs;
> + atomic_clearbits_int(&p->p_sigmask, bs);
> break;
>
> case LINUX_SIG_SETMASK:
> - p->p_sigmask = bs & ~sigcantmask;
> + proc_sigsetmask(p, bs &~ sigcantmask);
> break;
>
> default:
> @@ -624,8 +621,6 @@ linux_sys_sigprocmask(p, v, retval)
> break;
> }
>
> - splx(s);
> -
> return (error);
> }
>
> @@ -644,7 +639,6 @@ linux_sys_rt_sigprocmask(p, v, retval)
> linux_sigset_t ls;
> sigset_t bs;
> int error = 0;
> - int s;
>
> if (SCARG(uap, sigsetsize) != sizeof(linux_sigset_t))
> return (EINVAL);
> @@ -667,19 +661,17 @@ linux_sys_rt_sigprocmask(p, v, retval)
>
> linux_to_bsd_sigset(&ls, &bs);
>
> - s = splhigh();
> -
> switch (SCARG(uap, how)) {
> case LINUX_SIG_BLOCK:
> - p->p_sigmask |= bs & ~sigcantmask;
> + atomic_setbits_int(&p->p_sigmask, bs &~ sigcantmask);
> break;
>
> case LINUX_SIG_UNBLOCK:
> - p->p_sigmask &= ~bs;
> + atomic_clearbits_int(&p->p_sigmask, bs);
> break;
>
> case LINUX_SIG_SETMASK:
> - p->p_sigmask = bs & ~sigcantmask;
> + proc_sigsetmask(p, bs &~ sigcantmask);
> break;
>
> default:
> @@ -687,8 +679,6 @@ linux_sys_rt_sigprocmask(p, v, retval)
> break;
> }
>
> - splx(s);
> -
> return (error);
> }
>
> @@ -727,16 +717,13 @@ linux_sys_sigsetmask(p, v, retval)
> } */ *uap = v;
> linux_old_sigset_t mask;
> sigset_t bsdsig;
> - int s;
>
> bsd_to_linux_old_sigset(&p->p_sigmask, (linux_old_sigset_t *)retval);
>
> mask = SCARG(uap, mask);
> bsd_to_linux_old_sigset(&bsdsig, &mask);
>
> - s = splhigh();
> - p->p_sigmask = bsdsig & ~sigcantmask;
> - splx(s);
> + proc_sigsetmask(p, bsdsig &~ sigcantmask);
>
> return (0);
> }
> Index: arch/amd64/amd64/machdep.c
> ===================================================================
> RCS file: /home/matthew/cvs-mirror/cvs/src/sys/arch/amd64/amd64/machdep.c,v
> retrieving revision 1.180
> diff -u -p -r1.180 machdep.c
> --- arch/amd64/amd64/machdep.c 15 Jun 2014 11:43:24 -0000 1.180
> +++ arch/amd64/amd64/machdep.c 7 Jul 2014 19:24:07 -0000
> @@ -670,7 +670,7 @@ sys_sigreturn(struct proc *p, void *v, r
> bcopy(&ksc, tf, sizeof(*tf));
>
> /* Restore signal mask. */
> - p->p_sigmask = ksc.sc_mask & ~sigcantmask;
> + proc_sigsetmask(p, ksc.sc_mask & ~sigcantmask);
>
> /*
> * sigreturn() needs to return to userspace via the 'iretq'
> Index: arch/i386/i386/machdep.c
> ===================================================================
> RCS file: /home/matthew/cvs-mirror/cvs/src/sys/arch/i386/i386/machdep.c,v
> retrieving revision 1.539
> diff -u -p -r1.539 machdep.c
> --- arch/i386/i386/machdep.c 15 Jun 2014 11:43:24 -0000 1.539
> +++ arch/i386/i386/machdep.c 7 Jul 2014 19:24:07 -0000
> @@ -2491,7 +2491,7 @@ sys_sigreturn(struct proc *p, void *v, r
> p->p_md.md_flags |= MDP_USEDFPU;
> }
>
> - p->p_sigmask = context.sc_mask & ~sigcantmask;
> + proc_sigsetmask(p, context.sc_mask & ~sigcantmask);
>
> return (EJUSTRETURN);
> }
> @@ -3990,4 +3990,3 @@ intr_handler(struct intrframe *frame, st
> #endif
> return rc;
> }
> -
>