As 32-bit architectures now have two variants of pselect6, one using
32-bit time_t and one using 64-bit time_t, we unfortunately also need
to add a second compat case for the version of pselect6 that now
uses a 64-bit time_t but the 32-bit format for fd_set and sigset,
which is different from what a 64-bit kernel uses natively.

This is complicated by the way that poll_select_copy_remaining()
works, which already copies back the remaining time as either
timespec or timeval, depending on whether it is used by
select or pselect6. We extend it in a similar fashion to also
copy back a __kernel_timespec.

Signed-off-by: Arnd Bergmann <[email protected]>
---
 fs/compat.c | 62 ++++++++++++++++++++++++++++++++++++++++++-------------------
 1 file changed, 43 insertions(+), 19 deletions(-)

diff --git a/fs/compat.c b/fs/compat.c
index fac162df4b9b..e4fffe221d37 100644
--- a/fs/compat.c
+++ b/fs/compat.c
@@ -1075,7 +1075,7 @@ COMPAT_SYSCALL_DEFINE4(openat, int, dfd, const char 
__user *, filename, int, fla
 #define __COMPAT_NFDBITS       (8 * sizeof(compat_ulong_t))
 
 static int poll_select_copy_remaining(struct timespec64 *end_time, void __user 
*p,
-                                     int timeval, int ret)
+                                     int timeval, int compat_time, int ret)
 {
        struct timespec64 ts;
 
@@ -1102,14 +1102,10 @@ static int poll_select_copy_remaining(struct timespec64 
*end_time, void __user *
 
                if (!copy_to_user(p, &rtv, sizeof(rtv)))
                        return ret;
+       } else if (compat_time) {
+               return compat_put_timespec64(end_time, p);
        } else {
-               struct compat_timespec rts;
-
-               rts.tv_sec = ts.tv_sec;
-               rts.tv_nsec = ts.tv_nsec;
-
-               if (!copy_to_user(p, &rts, sizeof(rts)))
-                       return ret;
+               return put_timespec64(end_time, p);
        }
        /*
         * If an application puts its timeval in read-only memory, we
@@ -1297,7 +1293,7 @@ COMPAT_SYSCALL_DEFINE5(select, int, n, compat_ulong_t 
__user *, inp,
        }
 
        ret = compat_core_sys_select(n, inp, outp, exp, to);
-       ret = poll_select_copy_remaining(&end_time, tvp, 1, ret);
+       ret = poll_select_copy_remaining(&end_time, tvp, 1, 1, ret);
 
        return ret;
 }
@@ -1340,20 +1336,26 @@ int compat_copy_sigset_from_user(sigset_t *out, const 
compat_sigset_t __user *in
 #endif
        return 0;
 }
+#endif
 
 static long do_compat_pselect(int n, compat_ulong_t __user *inp,
        compat_ulong_t __user *outp, compat_ulong_t __user *exp,
-       struct compat_timespec __user *tsp, compat_sigset_t __user *sigmask,
-       compat_size_t sigsetsize)
+       void __user *tsp, compat_sigset_t __user *sigmask,
+       compat_size_t sigsetsize, int compat_time)
 {
        sigset_t ksigmask, sigsaved;
-       struct compat_timespec ts;
+       struct timespec64 ts;
        struct timespec64 end_time, *to = NULL;
        int ret;
 
        if (tsp) {
-               if (copy_from_user(&ts, tsp, sizeof(ts)))
-                       return -EFAULT;
+               if (compat_time) {
+                       if (compat_get_timespec64(&ts, tsp))
+                               return -EFAULT;
+               } else {
+                       if (get_timespec64(&ts, tsp))
+                               return -EFAULT;
+               }
 
                to = &end_time;
                if (poll_select_set_timeout(to, ts.tv_sec, ts.tv_nsec))
@@ -1369,7 +1371,7 @@ static long do_compat_pselect(int n, compat_ulong_t 
__user *inp,
        }
 
        ret = compat_core_sys_select(n, inp, outp, exp, to);
-       ret = poll_select_copy_remaining(&end_time, tsp, 0, ret);
+       ret = poll_select_copy_remaining(&end_time, tsp, 0, compat_time, ret);
 
        if (ret == -ERESTARTNOHAND) {
                /*
@@ -1388,6 +1390,7 @@ static long do_compat_pselect(int n, compat_ulong_t 
__user *inp,
        return ret;
 }
 
+#ifdef CONFIG_COMPAT_TIME
 COMPAT_SYSCALL_DEFINE6(pselect6, int, n, compat_ulong_t __user *, inp,
        compat_ulong_t __user *, outp, compat_ulong_t __user *, exp,
        struct compat_timespec __user *, tsp, void __user *, sig)
@@ -1398,13 +1401,13 @@ COMPAT_SYSCALL_DEFINE6(pselect6, int, n, compat_ulong_t 
__user *, inp,
        if (sig) {
                if (!access_ok(VERIFY_READ, sig,
                                sizeof(compat_uptr_t)+sizeof(compat_size_t)) ||
-                       __get_user(up, (compat_uptr_t __user *)sig) ||
-                       __get_user(sigsetsize,
+                               __get_user(up, (compat_uptr_t __user *)sig) ||
+                               __get_user(sigsetsize,
                                (compat_size_t __user *)(sig+sizeof(up))))
                        return -EFAULT;
        }
        return do_compat_pselect(n, inp, outp, exp, tsp, compat_ptr(up),
-                                sigsetsize);
+                                sigsetsize, 1);
 }
 
 COMPAT_SYSCALL_DEFINE5(ppoll, struct pollfd __user *, ufds,
@@ -1451,12 +1454,33 @@ COMPAT_SYSCALL_DEFINE5(ppoll, struct pollfd __user *, 
ufds,
        } else if (sigmask)
                sigprocmask(SIG_SETMASK, &sigsaved, NULL);
 
-       ret = poll_select_copy_remaining(&end_time, tsp, 0, ret);
+       ret = poll_select_copy_remaining(&end_time, tsp, 0, 1, ret);
 
        return ret;
 }
 #endif
 
+#ifdef CONFIG_COMPAT
+COMPAT_SYSCALL_DEFINE6(pselect6_time64, int, n, compat_ulong_t __user *, inp,
+       compat_ulong_t __user *, outp, compat_ulong_t __user *, exp,
+       struct __kernel_timespec __user *, tsp, void __user *, sig)
+{
+       compat_size_t sigsetsize = 0;
+       compat_uptr_t up = 0;
+
+       if (sig) {
+               if (!access_ok(VERIFY_READ, sig,
+                               sizeof(compat_uptr_t)+sizeof(compat_size_t)) ||
+                               __get_user(up, (compat_uptr_t __user *)sig) ||
+                               __get_user(sigsetsize,
+                               (compat_size_t __user *)(sig+sizeof(up))))
+                       return -EFAULT;
+       }
+       return do_compat_pselect(n, inp, outp, exp, tsp, compat_ptr(up),
+                                sigsetsize, 0);
+}
+#endif
+
 #if defined(CONFIG_FHANDLE) && defined(CONFIG_COMPAT)
 /*
  * Exactly like fs/open.c:sys_open_by_handle_at(), except that it
-- 
2.1.0.rc2

_______________________________________________
Y2038 mailing list
[email protected]
https://lists.linaro.org/mailman/listinfo/y2038

Reply via email to