signal: simplify set_user_sigmask/restore_user_sigmask
authorOleg Nesterov <oleg@redhat.com>
Tue, 16 Jul 2019 23:29:53 +0000 (16:29 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Wed, 17 Jul 2019 02:23:24 +0000 (19:23 -0700)
task->saved_sigmask and ->restore_sigmask are only used in the ret-from-
syscall paths.  This means that set_user_sigmask() can save ->blocked in
->saved_sigmask and do set_restore_sigmask() to indicate that ->blocked
was modified.

This way the callers do not need 2 sigset_t's passed to set/restore and
restore_user_sigmask() renamed to restore_saved_sigmask_unless() turns
into the trivial helper which just calls restore_saved_sigmask().

Link: http://lkml.kernel.org/r/20190606113206.GA9464@redhat.com
Signed-off-by: Oleg Nesterov <oleg@redhat.com>
Cc: Deepa Dinamani <deepa.kernel@gmail.com>
Cc: Arnd Bergmann <arnd@arndb.de>
Cc: Jens Axboe <axboe@kernel.dk>
Cc: Davidlohr Bueso <dave@stgolabs.net>
Cc: Eric Wong <e@80x24.org>
Cc: Jason Baron <jbaron@akamai.com>
Cc: Thomas Gleixner <tglx@linutronix.de>
Cc: Al Viro <viro@ZenIV.linux.org.uk>
Cc: Eric W. Biederman <ebiederm@xmission.com>
Cc: David Laight <David.Laight@aculab.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
fs/aio.c
fs/eventpoll.c
fs/io_uring.c
fs/select.c
include/linux/compat.h
include/linux/sched/signal.h
include/linux/signal.h
kernel/signal.c

index 2d405733a8c6e75b2ac0fa16cc129a138e033b65..8327db0c8e08392bef1a602b648f5d40c8572a83 100644 (file)
--- a/fs/aio.c
+++ b/fs/aio.c
@@ -2094,7 +2094,6 @@ SYSCALL_DEFINE6(io_pgetevents,
                const struct __aio_sigset __user *, usig)
 {
        struct __aio_sigset     ksig = { NULL, };
-       sigset_t                ksigmask, sigsaved;
        struct timespec64       ts;
        bool interrupted;
        int ret;
@@ -2105,14 +2104,14 @@ SYSCALL_DEFINE6(io_pgetevents,
        if (usig && copy_from_user(&ksig, usig, sizeof(ksig)))
                return -EFAULT;
 
-       ret = set_user_sigmask(ksig.sigmask, &ksigmask, &sigsaved, ksig.sigsetsize);
+       ret = set_user_sigmask(ksig.sigmask, ksig.sigsetsize);
        if (ret)
                return ret;
 
        ret = do_io_getevents(ctx_id, min_nr, nr, events, timeout ? &ts : NULL);
 
        interrupted = signal_pending(current);
-       restore_user_sigmask(ksig.sigmask, &sigsaved, interrupted);
+       restore_saved_sigmask_unless(interrupted);
        if (interrupted && !ret)
                ret = -ERESTARTNOHAND;
 
@@ -2130,7 +2129,6 @@ SYSCALL_DEFINE6(io_pgetevents_time32,
                const struct __aio_sigset __user *, usig)
 {
        struct __aio_sigset     ksig = { NULL, };
-       sigset_t                ksigmask, sigsaved;
        struct timespec64       ts;
        bool interrupted;
        int ret;
@@ -2142,14 +2140,14 @@ SYSCALL_DEFINE6(io_pgetevents_time32,
                return -EFAULT;
 
 
-       ret = set_user_sigmask(ksig.sigmask, &ksigmask, &sigsaved, ksig.sigsetsize);
+       ret = set_user_sigmask(ksig.sigmask, ksig.sigsetsize);
        if (ret)
                return ret;
 
        ret = do_io_getevents(ctx_id, min_nr, nr, events, timeout ? &ts : NULL);
 
        interrupted = signal_pending(current);
-       restore_user_sigmask(ksig.sigmask, &sigsaved, interrupted);
+       restore_saved_sigmask_unless(interrupted);
        if (interrupted && !ret)
                ret = -ERESTARTNOHAND;
 
@@ -2198,7 +2196,6 @@ COMPAT_SYSCALL_DEFINE6(io_pgetevents,
                const struct __compat_aio_sigset __user *, usig)
 {
        struct __compat_aio_sigset ksig = { NULL, };
-       sigset_t ksigmask, sigsaved;
        struct timespec64 t;
        bool interrupted;
        int ret;
@@ -2209,14 +2206,14 @@ COMPAT_SYSCALL_DEFINE6(io_pgetevents,
        if (usig && copy_from_user(&ksig, usig, sizeof(ksig)))
                return -EFAULT;
 
-       ret = set_compat_user_sigmask(ksig.sigmask, &ksigmask, &sigsaved, ksig.sigsetsize);
+       ret = set_compat_user_sigmask(ksig.sigmask, ksig.sigsetsize);
        if (ret)
                return ret;
 
        ret = do_io_getevents(ctx_id, min_nr, nr, events, timeout ? &t : NULL);
 
        interrupted = signal_pending(current);
-       restore_user_sigmask(ksig.sigmask, &sigsaved, interrupted);
+       restore_saved_sigmask_unless(interrupted);
        if (interrupted && !ret)
                ret = -ERESTARTNOHAND;
 
@@ -2234,7 +2231,6 @@ COMPAT_SYSCALL_DEFINE6(io_pgetevents_time64,
                const struct __compat_aio_sigset __user *, usig)
 {
        struct __compat_aio_sigset ksig = { NULL, };
-       sigset_t ksigmask, sigsaved;
        struct timespec64 t;
        bool interrupted;
        int ret;
@@ -2245,14 +2241,14 @@ COMPAT_SYSCALL_DEFINE6(io_pgetevents_time64,
        if (usig && copy_from_user(&ksig, usig, sizeof(ksig)))
                return -EFAULT;
 
-       ret = set_compat_user_sigmask(ksig.sigmask, &ksigmask, &sigsaved, ksig.sigsetsize);
+       ret = set_compat_user_sigmask(ksig.sigmask, ksig.sigsetsize);
        if (ret)
                return ret;
 
        ret = do_io_getevents(ctx_id, min_nr, nr, events, timeout ? &t : NULL);
 
        interrupted = signal_pending(current);
-       restore_user_sigmask(ksig.sigmask, &sigsaved, interrupted);
+       restore_saved_sigmask_unless(interrupted);
        if (interrupted && !ret)
                ret = -ERESTARTNOHAND;
 
index 4c74c768ae43932169d661ec6350282b452cd132..0f9c073d78d5ed2fababaeea22054f6de2c2b833 100644 (file)
@@ -2313,19 +2313,17 @@ SYSCALL_DEFINE6(epoll_pwait, int, epfd, struct epoll_event __user *, events,
                size_t, sigsetsize)
 {
        int error;
-       sigset_t ksigmask, sigsaved;
 
        /*
         * If the caller wants a certain signal mask to be set during the wait,
         * we apply it here.
         */
-       error = set_user_sigmask(sigmask, &ksigmask, &sigsaved, sigsetsize);
+       error = set_user_sigmask(sigmask, sigsetsize);
        if (error)
                return error;
 
        error = do_epoll_wait(epfd, events, maxevents, timeout);
-
-       restore_user_sigmask(sigmask, &sigsaved, error == -EINTR);
+       restore_saved_sigmask_unless(error == -EINTR);
 
        return error;
 }
@@ -2338,19 +2336,17 @@ COMPAT_SYSCALL_DEFINE6(epoll_pwait, int, epfd,
                        compat_size_t, sigsetsize)
 {
        long err;
-       sigset_t ksigmask, sigsaved;
 
        /*
         * If the caller wants a certain signal mask to be set during the wait,
         * we apply it here.
         */
-       err = set_compat_user_sigmask(sigmask, &ksigmask, &sigsaved, sigsetsize);
+       err = set_compat_user_sigmask(sigmask, sigsetsize);
        if (err)
                return err;
 
        err = do_epoll_wait(epfd, events, maxevents, timeout);
-
-       restore_user_sigmask(sigmask, &sigsaved, err == -EINTR);
+       restore_saved_sigmask_unless(err == -EINTR);
 
        return err;
 }
index d682049c07b2c0b6daa78bccaa0da81b2df4811b..e2a66e12fbc634f1eb913ef80f504dbd470edd69 100644 (file)
@@ -2400,7 +2400,6 @@ static int io_cqring_wait(struct io_ring_ctx *ctx, int min_events,
                          const sigset_t __user *sig, size_t sigsz)
 {
        struct io_cq_ring *ring = ctx->cq_ring;
-       sigset_t ksigmask, sigsaved;
        int ret;
 
        if (io_cqring_events(ring) >= min_events)
@@ -2410,21 +2409,17 @@ static int io_cqring_wait(struct io_ring_ctx *ctx, int min_events,
 #ifdef CONFIG_COMPAT
                if (in_compat_syscall())
                        ret = set_compat_user_sigmask((const compat_sigset_t __user *)sig,
-                                                     &ksigmask, &sigsaved, sigsz);
+                                                     sigsz);
                else
 #endif
-                       ret = set_user_sigmask(sig, &ksigmask,
-                                              &sigsaved, sigsz);
+                       ret = set_user_sigmask(sig, sigsz);
 
                if (ret)
                        return ret;
        }
 
        ret = wait_event_interruptible(ctx->wait, io_cqring_events(ring) >= min_events);
-
-       if (sig)
-               restore_user_sigmask(sig, &sigsaved, ret == -ERESTARTSYS);
-
+       restore_saved_sigmask_unless(ret == -ERESTARTSYS);
        if (ret == -ERESTARTSYS)
                ret = -EINTR;
 
index a4d8f6e8b63c82294b896d2979513bbf2f9e5595..1fc1b247fededd6eab5f1fa547755d4894cd11bc 100644 (file)
@@ -730,7 +730,6 @@ static long do_pselect(int n, fd_set __user *inp, fd_set __user *outp,
                       const sigset_t __user *sigmask, size_t sigsetsize,
                       enum poll_time_type type)
 {
-       sigset_t ksigmask, sigsaved;
        struct timespec64 ts, end_time, *to = NULL;
        int ret;
 
@@ -753,12 +752,12 @@ static long do_pselect(int n, fd_set __user *inp, fd_set __user *outp,
                        return -EINVAL;
        }
 
-       ret = set_user_sigmask(sigmask, &ksigmask, &sigsaved, sigsetsize);
+       ret = set_user_sigmask(sigmask, sigsetsize);
        if (ret)
                return ret;
 
        ret = core_sys_select(n, inp, outp, exp, to);
-       restore_user_sigmask(sigmask, &sigsaved, ret == -ERESTARTNOHAND);
+       restore_saved_sigmask_unless(ret == -ERESTARTNOHAND);
        ret = poll_select_copy_remaining(&end_time, tsp, type, ret);
 
        return ret;
@@ -1086,7 +1085,6 @@ SYSCALL_DEFINE5(ppoll, struct pollfd __user *, ufds, unsigned int, nfds,
                struct __kernel_timespec __user *, tsp, const sigset_t __user *, sigmask,
                size_t, sigsetsize)
 {
-       sigset_t ksigmask, sigsaved;
        struct timespec64 ts, end_time, *to = NULL;
        int ret;
 
@@ -1099,17 +1097,16 @@ SYSCALL_DEFINE5(ppoll, struct pollfd __user *, ufds, unsigned int, nfds,
                        return -EINVAL;
        }
 
-       ret = set_user_sigmask(sigmask, &ksigmask, &sigsaved, sigsetsize);
+       ret = set_user_sigmask(sigmask, sigsetsize);
        if (ret)
                return ret;
 
        ret = do_sys_poll(ufds, nfds, to);
 
-       restore_user_sigmask(sigmask, &sigsaved, ret == -EINTR);
+       restore_saved_sigmask_unless(ret == -EINTR);
        /* We can restart this syscall, usually */
        if (ret == -EINTR)
                ret = -ERESTARTNOHAND;
-
        ret = poll_select_copy_remaining(&end_time, tsp, PT_TIMESPEC, ret);
 
        return ret;
@@ -1121,7 +1118,6 @@ SYSCALL_DEFINE5(ppoll_time32, struct pollfd __user *, ufds, unsigned int, nfds,
                struct old_timespec32 __user *, tsp, const sigset_t __user *, sigmask,
                size_t, sigsetsize)
 {
-       sigset_t ksigmask, sigsaved;
        struct timespec64 ts, end_time, *to = NULL;
        int ret;
 
@@ -1134,17 +1130,16 @@ SYSCALL_DEFINE5(ppoll_time32, struct pollfd __user *, ufds, unsigned int, nfds,
                        return -EINVAL;
        }
 
-       ret = set_user_sigmask(sigmask, &ksigmask, &sigsaved, sigsetsize);
+       ret = set_user_sigmask(sigmask, sigsetsize);
        if (ret)
                return ret;
 
        ret = do_sys_poll(ufds, nfds, to);
 
-       restore_user_sigmask(sigmask, &sigsaved, ret == -EINTR);
+       restore_saved_sigmask_unless(ret == -EINTR);
        /* We can restart this syscall, usually */
        if (ret == -EINTR)
                ret = -ERESTARTNOHAND;
-
        ret = poll_select_copy_remaining(&end_time, tsp, PT_OLD_TIMESPEC, ret);
 
        return ret;
@@ -1319,7 +1314,6 @@ static long do_compat_pselect(int n, compat_ulong_t __user *inp,
        void __user *tsp, compat_sigset_t __user *sigmask,
        compat_size_t sigsetsize, enum poll_time_type type)
 {
-       sigset_t ksigmask, sigsaved;
        struct timespec64 ts, end_time, *to = NULL;
        int ret;
 
@@ -1342,12 +1336,12 @@ static long do_compat_pselect(int n, compat_ulong_t __user *inp,
                        return -EINVAL;
        }
 
-       ret = set_compat_user_sigmask(sigmask, &ksigmask, &sigsaved, sigsetsize);
+       ret = set_compat_user_sigmask(sigmask, sigsetsize);
        if (ret)
                return ret;
 
        ret = compat_core_sys_select(n, inp, outp, exp, to);
-       restore_user_sigmask(sigmask, &sigsaved, ret == -ERESTARTNOHAND);
+       restore_saved_sigmask_unless(ret == -ERESTARTNOHAND);
        ret = poll_select_copy_remaining(&end_time, tsp, type, ret);
 
        return ret;
@@ -1402,7 +1396,6 @@ COMPAT_SYSCALL_DEFINE5(ppoll_time32, struct pollfd __user *, ufds,
        unsigned int,  nfds, struct old_timespec32 __user *, tsp,
        const compat_sigset_t __user *, sigmask, compat_size_t, sigsetsize)
 {
-       sigset_t ksigmask, sigsaved;
        struct timespec64 ts, end_time, *to = NULL;
        int ret;
 
@@ -1415,17 +1408,16 @@ COMPAT_SYSCALL_DEFINE5(ppoll_time32, struct pollfd __user *, ufds,
                        return -EINVAL;
        }
 
-       ret = set_compat_user_sigmask(sigmask, &ksigmask, &sigsaved, sigsetsize);
+       ret = set_compat_user_sigmask(sigmask, sigsetsize);
        if (ret)
                return ret;
 
        ret = do_sys_poll(ufds, nfds, to);
 
-       restore_user_sigmask(sigmask, &sigsaved, ret == -EINTR);
+       restore_saved_sigmask_unless(ret == -EINTR);
        /* We can restart this syscall, usually */
        if (ret == -EINTR)
                ret = -ERESTARTNOHAND;
-
        ret = poll_select_copy_remaining(&end_time, tsp, PT_OLD_TIMESPEC, ret);
 
        return ret;
@@ -1437,7 +1429,6 @@ COMPAT_SYSCALL_DEFINE5(ppoll_time64, struct pollfd __user *, ufds,
        unsigned int,  nfds, struct __kernel_timespec __user *, tsp,
        const compat_sigset_t __user *, sigmask, compat_size_t, sigsetsize)
 {
-       sigset_t ksigmask, sigsaved;
        struct timespec64 ts, end_time, *to = NULL;
        int ret;
 
@@ -1450,17 +1441,16 @@ COMPAT_SYSCALL_DEFINE5(ppoll_time64, struct pollfd __user *, ufds,
                        return -EINVAL;
        }
 
-       ret = set_compat_user_sigmask(sigmask, &ksigmask, &sigsaved, sigsetsize);
+       ret = set_compat_user_sigmask(sigmask, sigsetsize);
        if (ret)
                return ret;
 
        ret = do_sys_poll(ufds, nfds, to);
 
-       restore_user_sigmask(sigmask, &sigsaved, ret == -EINTR);
+       restore_saved_sigmask_unless(ret == -EINTR);
        /* We can restart this syscall, usually */
        if (ret == -EINTR)
                ret = -ERESTARTNOHAND;
-
        ret = poll_select_copy_remaining(&end_time, tsp, PT_TIMESPEC, ret);
 
        return ret;
index ebddcb6cfcf8aac06014872547031fda6ac8d4f3..16dafd9f4b862832c00617f7a75b338fb6c25855 100644 (file)
@@ -138,8 +138,7 @@ typedef struct {
        compat_sigset_word      sig[_COMPAT_NSIG_WORDS];
 } compat_sigset_t;
 
-int set_compat_user_sigmask(const compat_sigset_t __user *usigmask,
-                           sigset_t *set, sigset_t *oldset,
+int set_compat_user_sigmask(const compat_sigset_t __user *umask,
                            size_t sigsetsize);
 
 struct compat_sigaction {
index 01add55a609b1ef65fa139d09f6746fb0b34f511..efd8ce7675ede96262d26138d9d2670fb37cefa3 100644 (file)
@@ -420,7 +420,6 @@ void task_join_group_stop(struct task_struct *task);
 static inline void set_restore_sigmask(void)
 {
        set_thread_flag(TIF_RESTORE_SIGMASK);
-       WARN_ON(!test_thread_flag(TIF_SIGPENDING));
 }
 
 static inline void clear_tsk_restore_sigmask(struct task_struct *task)
@@ -451,7 +450,6 @@ static inline bool test_and_clear_restore_sigmask(void)
 static inline void set_restore_sigmask(void)
 {
        current->restore_sigmask = true;
-       WARN_ON(!test_thread_flag(TIF_SIGPENDING));
 }
 static inline void clear_tsk_restore_sigmask(struct task_struct *task)
 {
@@ -484,6 +482,16 @@ static inline void restore_saved_sigmask(void)
                __set_current_blocked(&current->saved_sigmask);
 }
 
+extern int set_user_sigmask(const sigset_t __user *umask, size_t sigsetsize);
+
+static inline void restore_saved_sigmask_unless(bool interrupted)
+{
+       if (interrupted)
+               WARN_ON(!test_thread_flag(TIF_SIGPENDING));
+       else
+               restore_saved_sigmask();
+}
+
 static inline sigset_t *sigmask_to_save(void)
 {
        sigset_t *res = &current->blocked;
index 78c2bb37695469d22b0f14e296623ae2888d56fe..b5d99482d3fe18e5f9c2eceb606a75458ae7e969 100644 (file)
@@ -273,10 +273,6 @@ extern int group_send_sig_info(int sig, struct kernel_siginfo *info,
                               struct task_struct *p, enum pid_type type);
 extern int __group_send_sig_info(int, struct kernel_siginfo *, struct task_struct *);
 extern int sigprocmask(int, sigset_t *, sigset_t *);
-extern int set_user_sigmask(const sigset_t __user *usigmask, sigset_t *set,
-       sigset_t *oldset, size_t sigsetsize);
-extern void restore_user_sigmask(const void __user *usigmask,
-                                sigset_t *sigsaved, bool interrupted);
 extern void set_current_blocked(sigset_t *);
 extern void __set_current_blocked(const sigset_t *);
 extern int show_unhandled_signals;
index dabe100d209191cb6b15a6f80910e92b28553f11..91b789dd6e722ef96f8cf4440eb8f5bbc857ac60 100644 (file)
@@ -2951,80 +2951,49 @@ EXPORT_SYMBOL(sigprocmask);
  *
  * This is useful for syscalls such as ppoll, pselect, io_pgetevents and
  * epoll_pwait where a new sigmask is passed from userland for the syscalls.
+ *
+ * Note that it does set_restore_sigmask() in advance, so it must be always
+ * paired with restore_saved_sigmask_unless() before return from syscall.
  */
-int set_user_sigmask(const sigset_t __user *usigmask, sigset_t *set,
-                    sigset_t *oldset, size_t sigsetsize)
+int set_user_sigmask(const sigset_t __user *umask, size_t sigsetsize)
 {
-       if (!usigmask)
-               return 0;
+       sigset_t kmask;
 
+       if (!umask)
+               return 0;
        if (sigsetsize != sizeof(sigset_t))
                return -EINVAL;
-       if (copy_from_user(set, usigmask, sizeof(sigset_t)))
+       if (copy_from_user(&kmask, umask, sizeof(sigset_t)))
                return -EFAULT;
 
-       *oldset = current->blocked;
-       set_current_blocked(set);
+       set_restore_sigmask();
+       current->saved_sigmask = current->blocked;
+       set_current_blocked(&kmask);
 
        return 0;
 }
-EXPORT_SYMBOL(set_user_sigmask);
 
 #ifdef CONFIG_COMPAT
-int set_compat_user_sigmask(const compat_sigset_t __user *usigmask,
-                           sigset_t *set, sigset_t *oldset,
+int set_compat_user_sigmask(const compat_sigset_t __user *umask,
                            size_t sigsetsize)
 {
-       if (!usigmask)
-               return 0;
+       sigset_t kmask;
 
+       if (!umask)
+               return 0;
        if (sigsetsize != sizeof(compat_sigset_t))
                return -EINVAL;
-       if (get_compat_sigset(set, usigmask))
+       if (get_compat_sigset(&kmask, umask))
                return -EFAULT;
 
-       *oldset = current->blocked;
-       set_current_blocked(set);
+       set_restore_sigmask();
+       current->saved_sigmask = current->blocked;
+       set_current_blocked(&kmask);
 
        return 0;
 }
-EXPORT_SYMBOL(set_compat_user_sigmask);
 #endif
 
-/*
- * restore_user_sigmask:
- * usigmask: sigmask passed in from userland.
- * sigsaved: saved sigmask when the syscall started and changed the sigmask to
- *           usigmask.
- *
- * This is useful for syscalls such as ppoll, pselect, io_pgetevents and
- * epoll_pwait where a new sigmask is passed in from userland for the syscalls.
- */
-void restore_user_sigmask(const void __user *usigmask, sigset_t *sigsaved,
-                               bool interrupted)
-{
-
-       if (!usigmask)
-               return;
-       /*
-        * When signals are pending, do not restore them here.
-        * Restoring sigmask here can lead to delivering signals that the above
-        * syscalls are intended to block because of the sigmask passed in.
-        */
-       if (interrupted) {
-               current->saved_sigmask = *sigsaved;
-               set_restore_sigmask();
-               return;
-       }
-
-       /*
-        * This is needed because the fast syscall return path does not restore
-        * saved_sigmask when signals are not pending.
-        */
-       set_current_blocked(sigsaved);
-}
-EXPORT_SYMBOL(restore_user_sigmask);
-
 /**
  *  sys_rt_sigprocmask - change the list of currently blocked signals
  *  @how: whether to add, remove, or set signals