signal: Add restore_user_sigmask()
authorDeepa Dinamani <deepa.kernel@gmail.com>
Thu, 20 Sep 2018 04:41:05 +0000 (21:41 -0700)
committerArnd Bergmann <arnd@arndb.de>
Thu, 6 Dec 2018 16:22:53 +0000 (17:22 +0100)
Refactor the logic to restore the sigmask before the syscall
returns into an api.
This is useful for versions of syscalls that pass in the
sigmask and expect the current->sigmask to be changed during
the execution and restored after the execution of the syscall.

With the advent of new y2038 syscalls in the subsequent patches,
we add two more new versions of the syscalls (for pselect, ppoll
and io_pgetevents) in addition to the existing native and compat
versions. Adding such an api reduces the logic that would need to
be replicated otherwise.

Signed-off-by: Deepa Dinamani <deepa.kernel@gmail.com>
Signed-off-by: Arnd Bergmann <arnd@arndb.de>
fs/aio.c
fs/eventpoll.c
fs/select.c
include/linux/signal.h
kernel/signal.c

index 6ddb63ee8eb61ced737e9586833ad666767db4e6..39a1f2df6805e9717cb06ffb0cdef11be63e8fa7 100644 (file)
--- a/fs/aio.c
+++ b/fs/aio.c
@@ -2110,18 +2110,9 @@ SYSCALL_DEFINE6(io_pgetevents,
                return ret;
 
        ret = do_io_getevents(ctx_id, min_nr, nr, events, timeout ? &ts : NULL);
-       if (signal_pending(current)) {
-               if (ksig.sigmask) {
-                       current->saved_sigmask = sigsaved;
-                       set_restore_sigmask();
-               }
-
-               if (!ret)
-                       ret = -ERESTARTNOHAND;
-       } else {
-               if (ksig.sigmask)
-                       sigprocmask(SIG_SETMASK, &sigsaved, NULL);
-       }
+       restore_user_sigmask(ksig.sigmask, &sigsaved);
+       if (signal_pending(current) && !ret)
+               ret = -ERESTARTNOHAND;
 
        return ret;
 }
@@ -2175,17 +2166,9 @@ COMPAT_SYSCALL_DEFINE6(io_pgetevents,
                return ret;
 
        ret = do_io_getevents(ctx_id, min_nr, nr, events, timeout ? &t : NULL);
-       if (signal_pending(current)) {
-               if (ksig.sigmask) {
-                       current->saved_sigmask = sigsaved;
-                       set_restore_sigmask();
-               }
-               if (!ret)
-                       ret = -ERESTARTNOHAND;
-       } else {
-               if (ksig.sigmask)
-                       sigprocmask(SIG_SETMASK, &sigsaved, NULL);
-       }
+       restore_user_sigmask(ksig.sigmask, &sigsaved);
+       if (signal_pending(current) && !ret)
+               ret = -ERESTARTNOHAND;
 
        return ret;
 }
index 2d86eeba837b958fe7cb8705aea6d45a9ca31bb0..8a5a1010886bad1f5c2918565807b5b3aaa988e1 100644 (file)
@@ -2229,20 +2229,7 @@ SYSCALL_DEFINE6(epoll_pwait, int, epfd, struct epoll_event __user *, events,
 
        error = do_epoll_wait(epfd, events, maxevents, timeout);
 
-       /*
-        * If we changed the signal mask, we need to restore the original one.
-        * In case we've got a signal while waiting, we do not restore the
-        * signal mask yet, and we allow do_signal() to deliver the signal on
-        * the way back to userspace, before the signal mask is restored.
-        */
-       if (sigmask) {
-               if (error == -EINTR) {
-                       memcpy(&current->saved_sigmask, &sigsaved,
-                              sizeof(sigsaved));
-                       set_restore_sigmask();
-               } else
-                       set_current_blocked(&sigsaved);
-       }
+       restore_user_sigmask(sigmask, &sigsaved);
 
        return error;
 }
@@ -2267,20 +2254,7 @@ COMPAT_SYSCALL_DEFINE6(epoll_pwait, int, epfd,
 
        err = do_epoll_wait(epfd, events, maxevents, timeout);
 
-       /*
-        * If we changed the signal mask, we need to restore the original one.
-        * In case we've got a signal while waiting, we do not restore the
-        * signal mask yet, and we allow do_signal() to deliver the signal on
-        * the way back to userspace, before the signal mask is restored.
-        */
-       if (sigmask) {
-               if (err == -EINTR) {
-                       memcpy(&current->saved_sigmask, &sigsaved,
-                              sizeof(sigsaved));
-                       set_restore_sigmask();
-               } else
-                       set_current_blocked(&sigsaved);
-       }
+       restore_user_sigmask(sigmask, &sigsaved);
 
        return err;
 }
index 65c78b4147a2ab4958196aaf901e4ba5ca6b6494..eb91325201972465cea042710cd6b19ad77d7b26 100644 (file)
@@ -724,19 +724,7 @@ static long do_pselect(int n, fd_set __user *inp, fd_set __user *outp,
        ret = core_sys_select(n, inp, outp, exp, to);
        ret = poll_select_copy_remaining(&end_time, tsp, 0, ret);
 
-       if (ret == -ERESTARTNOHAND) {
-               /*
-                * Don't restore the signal mask yet. Let do_signal() deliver
-                * the signal on the way back to userspace, before the signal
-                * mask is restored.
-                */
-               if (sigmask) {
-                       memcpy(&current->saved_sigmask, &sigsaved,
-                                       sizeof(sigsaved));
-                       set_restore_sigmask();
-               }
-       } else if (sigmask)
-               sigprocmask(SIG_SETMASK, &sigsaved, NULL);
+       restore_user_sigmask(sigmask, &sigsaved);
 
        return ret;
 }
@@ -1060,21 +1048,11 @@ SYSCALL_DEFINE5(ppoll, struct pollfd __user *, ufds, unsigned int, nfds,
 
        ret = do_sys_poll(ufds, nfds, to);
 
+       restore_user_sigmask(sigmask, &sigsaved);
+
        /* We can restart this syscall, usually */
-       if (ret == -EINTR) {
-               /*
-                * Don't restore the signal mask yet. Let do_signal() deliver
-                * the signal on the way back to userspace, before the signal
-                * mask is restored.
-                */
-               if (sigmask) {
-                       memcpy(&current->saved_sigmask, &sigsaved,
-                                       sizeof(sigsaved));
-                       set_restore_sigmask();
-               }
+       if (ret == -EINTR)
                ret = -ERESTARTNOHAND;
-       } else if (sigmask)
-               sigprocmask(SIG_SETMASK, &sigsaved, NULL);
 
        ret = poll_select_copy_remaining(&end_time, tsp, 0, ret);
 
@@ -1316,19 +1294,7 @@ static long do_compat_pselect(int n, compat_ulong_t __user *inp,
        ret = compat_core_sys_select(n, inp, outp, exp, to);
        ret = compat_poll_select_copy_remaining(&end_time, tsp, 0, ret);
 
-       if (ret == -ERESTARTNOHAND) {
-               /*
-                * Don't restore the signal mask yet. Let do_signal() deliver
-                * the signal on the way back to userspace, before the signal
-                * mask is restored.
-                */
-               if (sigmask) {
-                       memcpy(&current->saved_sigmask, &sigsaved,
-                                       sizeof(sigsaved));
-                       set_restore_sigmask();
-               }
-       } else if (sigmask)
-               sigprocmask(SIG_SETMASK, &sigsaved, NULL);
+       restore_user_sigmask(sigmask, &sigsaved);
 
        return ret;
 }
@@ -1375,21 +1341,11 @@ COMPAT_SYSCALL_DEFINE5(ppoll, struct pollfd __user *, ufds,
 
        ret = do_sys_poll(ufds, nfds, to);
 
+       restore_user_sigmask(sigmask, &sigsaved);
+
        /* We can restart this syscall, usually */
-       if (ret == -EINTR) {
-               /*
-                * Don't restore the signal mask yet. Let do_signal() deliver
-                * the signal on the way back to userspace, before the signal
-                * mask is restored.
-                */
-               if (sigmask) {
-                       memcpy(&current->saved_sigmask, &sigsaved,
-                               sizeof(sigsaved));
-                       set_restore_sigmask();
-               }
+       if (ret == -EINTR)
                ret = -ERESTARTNOHAND;
-       } else if (sigmask)
-               sigprocmask(SIG_SETMASK, &sigsaved, NULL);
 
        ret = compat_poll_select_copy_remaining(&end_time, tsp, 0, ret);
 
index ce14b951befb8b1080e1a228a957a437a1279b36..cc7e2c1cd44465926c3626a014dbd051319c2a64 100644 (file)
@@ -275,6 +275,8 @@ extern int __group_send_sig_info(int, struct kernel_siginfo *, struct task_struc
 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);
 extern void set_current_blocked(sigset_t *);
 extern void __set_current_blocked(const sigset_t *);
 extern int show_unhandled_signals;
index 811b5d4406179900fecaace5b00f4bcd819fe98f..3c8ea7a328e0d8ada03b2eee1a144b4b1ab291a3 100644 (file)
@@ -2780,6 +2780,39 @@ int set_compat_user_sigmask(const compat_sigset_t __user *usigmask,
 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)
+{
+
+       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 (signal_pending(current)) {
+               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