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 arnd@arndb.de --- 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