diff options
author | Arnd Bergmann <arnd@arndb.de> | 2018-04-18 07:43:52 -0400 |
---|---|---|
committer | Arnd Bergmann <arnd@arndb.de> | 2018-12-18 10:13:04 -0500 |
commit | e11d4284e2f4de5048c6d1787c82226f0a198292 (patch) | |
tree | bc52de794fd0fc90e2842a9d680239d6c3e9c215 | |
parent | bec2f7cbb73eadf5e1cc7d54ecb0980ede244257 (diff) |
y2038: socket: Add compat_sys_recvmmsg_time64
recvmmsg() takes two arguments to pointers of structures that differ
between 32-bit and 64-bit architectures: mmsghdr and timespec.
For y2038 compatbility, we are changing the native system call from
timespec to __kernel_timespec with a 64-bit time_t (in another patch),
and use the existing compat system call on both 32-bit and 64-bit
architectures for compatibility with traditional 32-bit user space.
As we now have two variants of recvmmsg() for 32-bit tasks that are both
different from the variant that we use on 64-bit tasks, this means we
also require two compat system calls!
The solution I picked is to flip things around: The existing
compat_sys_recvmmsg() call gets moved from net/compat.c into net/socket.c
and now handles the case for old user space on all architectures that
have set CONFIG_COMPAT_32BIT_TIME. A new compat_sys_recvmmsg_time64()
call gets added in the old place for 64-bit architectures only, this
one handles the case of a compat mmsghdr structure combined with
__kernel_timespec.
In the indirect sys_socketcall(), we now need to call either
do_sys_recvmmsg() or __compat_sys_recvmmsg(), depending on what kind of
architecture we are on. For compat_sys_socketcall(), no such change is
needed, we always call __compat_sys_recvmmsg().
I decided to not add a new SYS_RECVMMSG_TIME64 socketcall: Any libc
implementation for 64-bit time_t will need significant changes including
an updated asm/unistd.h, and it seems better to consistently use the
separate syscalls that configuration, leaving the socketcall only for
backward compatibility with 32-bit time_t based libc.
The naming is asymmetric for the moment, so both existing syscalls
entry points keep their names, while the new ones are recvmmsg_time32
and compat_recvmmsg_time64 respectively. I expect that we will rename
the compat syscalls later as we start using generated syscall tables
everywhere and add these entry points.
Signed-off-by: Arnd Bergmann <arnd@arndb.de>
-rw-r--r-- | include/linux/compat.h | 3 | ||||
-rw-r--r-- | include/linux/socket.h | 9 | ||||
-rw-r--r-- | include/linux/syscalls.h | 3 | ||||
-rw-r--r-- | kernel/sys_ni.c | 2 | ||||
-rw-r--r-- | net/compat.c | 34 | ||||
-rw-r--r-- | net/socket.c | 62 |
6 files changed, 72 insertions, 41 deletions
diff --git a/include/linux/compat.h b/include/linux/compat.h index 8be8daa38c9a..4b0463608589 100644 --- a/include/linux/compat.h +++ b/include/linux/compat.h | |||
@@ -893,6 +893,9 @@ asmlinkage long compat_sys_move_pages(pid_t pid, compat_ulong_t nr_pages, | |||
893 | asmlinkage long compat_sys_rt_tgsigqueueinfo(compat_pid_t tgid, | 893 | asmlinkage long compat_sys_rt_tgsigqueueinfo(compat_pid_t tgid, |
894 | compat_pid_t pid, int sig, | 894 | compat_pid_t pid, int sig, |
895 | struct compat_siginfo __user *uinfo); | 895 | struct compat_siginfo __user *uinfo); |
896 | asmlinkage long compat_sys_recvmmsg_time64(int fd, struct compat_mmsghdr __user *mmsg, | ||
897 | unsigned vlen, unsigned int flags, | ||
898 | struct __kernel_timespec __user *timeout); | ||
896 | asmlinkage long compat_sys_recvmmsg(int fd, struct compat_mmsghdr __user *mmsg, | 899 | asmlinkage long compat_sys_recvmmsg(int fd, struct compat_mmsghdr __user *mmsg, |
897 | unsigned vlen, unsigned int flags, | 900 | unsigned vlen, unsigned int flags, |
898 | struct old_timespec32 __user *timeout); | 901 | struct old_timespec32 __user *timeout); |
diff --git a/include/linux/socket.h b/include/linux/socket.h index 8b571e9b9f76..333b5df8a1b2 100644 --- a/include/linux/socket.h +++ b/include/linux/socket.h | |||
@@ -348,7 +348,8 @@ struct ucred { | |||
348 | extern int move_addr_to_kernel(void __user *uaddr, int ulen, struct sockaddr_storage *kaddr); | 348 | extern int move_addr_to_kernel(void __user *uaddr, int ulen, struct sockaddr_storage *kaddr); |
349 | extern int put_cmsg(struct msghdr*, int level, int type, int len, void *data); | 349 | extern int put_cmsg(struct msghdr*, int level, int type, int len, void *data); |
350 | 350 | ||
351 | struct timespec64; | 351 | struct __kernel_timespec; |
352 | struct old_timespec32; | ||
352 | 353 | ||
353 | /* The __sys_...msg variants allow MSG_CMSG_COMPAT iff | 354 | /* The __sys_...msg variants allow MSG_CMSG_COMPAT iff |
354 | * forbid_cmsg_compat==false | 355 | * forbid_cmsg_compat==false |
@@ -357,8 +358,10 @@ extern long __sys_recvmsg(int fd, struct user_msghdr __user *msg, | |||
357 | unsigned int flags, bool forbid_cmsg_compat); | 358 | unsigned int flags, bool forbid_cmsg_compat); |
358 | extern long __sys_sendmsg(int fd, struct user_msghdr __user *msg, | 359 | extern long __sys_sendmsg(int fd, struct user_msghdr __user *msg, |
359 | unsigned int flags, bool forbid_cmsg_compat); | 360 | unsigned int flags, bool forbid_cmsg_compat); |
360 | extern int __sys_recvmmsg(int fd, struct mmsghdr __user *mmsg, unsigned int vlen, | 361 | extern int __sys_recvmmsg(int fd, struct mmsghdr __user *mmsg, |
361 | unsigned int flags, struct timespec64 *timeout); | 362 | unsigned int vlen, unsigned int flags, |
363 | struct __kernel_timespec __user *timeout, | ||
364 | struct old_timespec32 __user *timeout32); | ||
362 | extern int __sys_sendmmsg(int fd, struct mmsghdr __user *mmsg, | 365 | extern int __sys_sendmmsg(int fd, struct mmsghdr __user *mmsg, |
363 | unsigned int vlen, unsigned int flags, | 366 | unsigned int vlen, unsigned int flags, |
364 | bool forbid_cmsg_compat); | 367 | bool forbid_cmsg_compat); |
diff --git a/include/linux/syscalls.h b/include/linux/syscalls.h index 247ad9eca955..03cda6793be3 100644 --- a/include/linux/syscalls.h +++ b/include/linux/syscalls.h | |||
@@ -843,6 +843,9 @@ asmlinkage long sys_accept4(int, struct sockaddr __user *, int __user *, int); | |||
843 | asmlinkage long sys_recvmmsg(int fd, struct mmsghdr __user *msg, | 843 | asmlinkage long sys_recvmmsg(int fd, struct mmsghdr __user *msg, |
844 | unsigned int vlen, unsigned flags, | 844 | unsigned int vlen, unsigned flags, |
845 | struct __kernel_timespec __user *timeout); | 845 | struct __kernel_timespec __user *timeout); |
846 | asmlinkage long sys_recvmmsg_time32(int fd, struct mmsghdr __user *msg, | ||
847 | unsigned int vlen, unsigned flags, | ||
848 | struct old_timespec32 __user *timeout); | ||
846 | 849 | ||
847 | asmlinkage long sys_wait4(pid_t pid, int __user *stat_addr, | 850 | asmlinkage long sys_wait4(pid_t pid, int __user *stat_addr, |
848 | int options, struct rusage __user *ru); | 851 | int options, struct rusage __user *ru); |
diff --git a/kernel/sys_ni.c b/kernel/sys_ni.c index df556175be50..ab9d0e3c6d50 100644 --- a/kernel/sys_ni.c +++ b/kernel/sys_ni.c | |||
@@ -284,7 +284,9 @@ COND_SYSCALL_COMPAT(move_pages); | |||
284 | COND_SYSCALL(perf_event_open); | 284 | COND_SYSCALL(perf_event_open); |
285 | COND_SYSCALL(accept4); | 285 | COND_SYSCALL(accept4); |
286 | COND_SYSCALL(recvmmsg); | 286 | COND_SYSCALL(recvmmsg); |
287 | COND_SYSCALL(recvmmsg_time32); | ||
287 | COND_SYSCALL_COMPAT(recvmmsg); | 288 | COND_SYSCALL_COMPAT(recvmmsg); |
289 | COND_SYSCALL_COMPAT(recvmmsg_time64); | ||
288 | 290 | ||
289 | /* | 291 | /* |
290 | * Architecture specific syscalls: see further below | 292 | * Architecture specific syscalls: see further below |
diff --git a/net/compat.c b/net/compat.c index 47a614b370cd..f7084780a8f8 100644 --- a/net/compat.c +++ b/net/compat.c | |||
@@ -810,34 +810,23 @@ COMPAT_SYSCALL_DEFINE6(recvfrom, int, fd, void __user *, buf, compat_size_t, len | |||
810 | return __compat_sys_recvfrom(fd, buf, len, flags, addr, addrlen); | 810 | return __compat_sys_recvfrom(fd, buf, len, flags, addr, addrlen); |
811 | } | 811 | } |
812 | 812 | ||
813 | static int __compat_sys_recvmmsg(int fd, struct compat_mmsghdr __user *mmsg, | 813 | COMPAT_SYSCALL_DEFINE5(recvmmsg_time64, int, fd, struct compat_mmsghdr __user *, mmsg, |
814 | unsigned int vlen, unsigned int flags, | 814 | unsigned int, vlen, unsigned int, flags, |
815 | struct old_timespec32 __user *timeout) | 815 | struct __kernel_timespec __user *, timeout) |
816 | { | 816 | { |
817 | int datagrams; | 817 | return __sys_recvmmsg(fd, (struct mmsghdr __user *)mmsg, vlen, |
818 | struct timespec64 ktspec; | 818 | flags | MSG_CMSG_COMPAT, timeout, NULL); |
819 | |||
820 | if (timeout == NULL) | ||
821 | return __sys_recvmmsg(fd, (struct mmsghdr __user *)mmsg, vlen, | ||
822 | flags | MSG_CMSG_COMPAT, NULL); | ||
823 | |||
824 | if (compat_get_timespec64(&ktspec, timeout)) | ||
825 | return -EFAULT; | ||
826 | |||
827 | datagrams = __sys_recvmmsg(fd, (struct mmsghdr __user *)mmsg, vlen, | ||
828 | flags | MSG_CMSG_COMPAT, &ktspec); | ||
829 | if (datagrams > 0 && compat_put_timespec64(&ktspec, timeout)) | ||
830 | datagrams = -EFAULT; | ||
831 | |||
832 | return datagrams; | ||
833 | } | 819 | } |
834 | 820 | ||
821 | #ifdef CONFIG_COMPAT_32BIT_TIME | ||
835 | COMPAT_SYSCALL_DEFINE5(recvmmsg, int, fd, struct compat_mmsghdr __user *, mmsg, | 822 | COMPAT_SYSCALL_DEFINE5(recvmmsg, int, fd, struct compat_mmsghdr __user *, mmsg, |
836 | unsigned int, vlen, unsigned int, flags, | 823 | unsigned int, vlen, unsigned int, flags, |
837 | struct old_timespec32 __user *, timeout) | 824 | struct old_timespec32 __user *, timeout) |
838 | { | 825 | { |
839 | return __compat_sys_recvmmsg(fd, mmsg, vlen, flags, timeout); | 826 | return __sys_recvmmsg(fd, (struct mmsghdr __user *)mmsg, vlen, |
827 | flags | MSG_CMSG_COMPAT, NULL, timeout); | ||
840 | } | 828 | } |
829 | #endif | ||
841 | 830 | ||
842 | COMPAT_SYSCALL_DEFINE2(socketcall, int, call, u32 __user *, args) | 831 | COMPAT_SYSCALL_DEFINE2(socketcall, int, call, u32 __user *, args) |
843 | { | 832 | { |
@@ -925,8 +914,9 @@ COMPAT_SYSCALL_DEFINE2(socketcall, int, call, u32 __user *, args) | |||
925 | ret = __compat_sys_recvmsg(a0, compat_ptr(a1), a[2]); | 914 | ret = __compat_sys_recvmsg(a0, compat_ptr(a1), a[2]); |
926 | break; | 915 | break; |
927 | case SYS_RECVMMSG: | 916 | case SYS_RECVMMSG: |
928 | ret = __compat_sys_recvmmsg(a0, compat_ptr(a1), a[2], a[3], | 917 | ret = __sys_recvmmsg(a0, compat_ptr(a1), a[2], |
929 | compat_ptr(a[4])); | 918 | a[3] | MSG_CMSG_COMPAT, NULL, |
919 | compat_ptr(a[4])); | ||
930 | break; | 920 | break; |
931 | case SYS_ACCEPT4: | 921 | case SYS_ACCEPT4: |
932 | ret = __sys_accept4(a0, compat_ptr(a1), compat_ptr(a[2]), a[3]); | 922 | ret = __sys_accept4(a0, compat_ptr(a1), compat_ptr(a[2]), a[3]); |
diff --git a/net/socket.c b/net/socket.c index 593826e11a53..f137a96628f1 100644 --- a/net/socket.c +++ b/net/socket.c | |||
@@ -2341,8 +2341,9 @@ SYSCALL_DEFINE3(recvmsg, int, fd, struct user_msghdr __user *, msg, | |||
2341 | * Linux recvmmsg interface | 2341 | * Linux recvmmsg interface |
2342 | */ | 2342 | */ |
2343 | 2343 | ||
2344 | int __sys_recvmmsg(int fd, struct mmsghdr __user *mmsg, unsigned int vlen, | 2344 | static int do_recvmmsg(int fd, struct mmsghdr __user *mmsg, |
2345 | unsigned int flags, struct timespec64 *timeout) | 2345 | unsigned int vlen, unsigned int flags, |
2346 | struct timespec64 *timeout) | ||
2346 | { | 2347 | { |
2347 | int fput_needed, err, datagrams; | 2348 | int fput_needed, err, datagrams; |
2348 | struct socket *sock; | 2349 | struct socket *sock; |
@@ -2451,25 +2452,32 @@ out_put: | |||
2451 | return datagrams; | 2452 | return datagrams; |
2452 | } | 2453 | } |
2453 | 2454 | ||
2454 | static int do_sys_recvmmsg(int fd, struct mmsghdr __user *mmsg, | 2455 | int __sys_recvmmsg(int fd, struct mmsghdr __user *mmsg, |
2455 | unsigned int vlen, unsigned int flags, | 2456 | unsigned int vlen, unsigned int flags, |
2456 | struct __kernel_timespec __user *timeout) | 2457 | struct __kernel_timespec __user *timeout, |
2458 | struct old_timespec32 __user *timeout32) | ||
2457 | { | 2459 | { |
2458 | int datagrams; | 2460 | int datagrams; |
2459 | struct timespec64 timeout_sys; | 2461 | struct timespec64 timeout_sys; |
2460 | 2462 | ||
2461 | if (flags & MSG_CMSG_COMPAT) | 2463 | if (timeout && get_timespec64(&timeout_sys, timeout)) |
2462 | return -EINVAL; | 2464 | return -EFAULT; |
2463 | |||
2464 | if (!timeout) | ||
2465 | return __sys_recvmmsg(fd, mmsg, vlen, flags, NULL); | ||
2466 | 2465 | ||
2467 | if (get_timespec64(&timeout_sys, timeout)) | 2466 | if (timeout32 && get_old_timespec32(&timeout_sys, timeout32)) |
2468 | return -EFAULT; | 2467 | return -EFAULT; |
2469 | 2468 | ||
2470 | datagrams = __sys_recvmmsg(fd, mmsg, vlen, flags, &timeout_sys); | 2469 | if (!timeout && !timeout32) |
2470 | return do_recvmmsg(fd, mmsg, vlen, flags, NULL); | ||
2471 | |||
2472 | datagrams = do_recvmmsg(fd, mmsg, vlen, flags, &timeout_sys); | ||
2471 | 2473 | ||
2472 | if (datagrams > 0 && put_timespec64(&timeout_sys, timeout)) | 2474 | if (datagrams <= 0) |
2475 | return datagrams; | ||
2476 | |||
2477 | if (timeout && put_timespec64(&timeout_sys, timeout)) | ||
2478 | datagrams = -EFAULT; | ||
2479 | |||
2480 | if (timeout32 && put_old_timespec32(&timeout_sys, timeout32)) | ||
2473 | datagrams = -EFAULT; | 2481 | datagrams = -EFAULT; |
2474 | 2482 | ||
2475 | return datagrams; | 2483 | return datagrams; |
@@ -2479,8 +2487,23 @@ SYSCALL_DEFINE5(recvmmsg, int, fd, struct mmsghdr __user *, mmsg, | |||
2479 | unsigned int, vlen, unsigned int, flags, | 2487 | unsigned int, vlen, unsigned int, flags, |
2480 | struct __kernel_timespec __user *, timeout) | 2488 | struct __kernel_timespec __user *, timeout) |
2481 | { | 2489 | { |
2482 | return do_sys_recvmmsg(fd, mmsg, vlen, flags, timeout); | 2490 | if (flags & MSG_CMSG_COMPAT) |
2491 | return -EINVAL; | ||
2492 | |||
2493 | return __sys_recvmmsg(fd, mmsg, vlen, flags, timeout, NULL); | ||
2494 | } | ||
2495 | |||
2496 | #ifdef CONFIG_COMPAT_32BIT_TIME | ||
2497 | SYSCALL_DEFINE5(recvmmsg_time32, int, fd, struct mmsghdr __user *, mmsg, | ||
2498 | unsigned int, vlen, unsigned int, flags, | ||
2499 | struct old_timespec32 __user *, timeout) | ||
2500 | { | ||
2501 | if (flags & MSG_CMSG_COMPAT) | ||
2502 | return -EINVAL; | ||
2503 | |||
2504 | return __sys_recvmmsg(fd, mmsg, vlen, flags, NULL, timeout); | ||
2483 | } | 2505 | } |
2506 | #endif | ||
2484 | 2507 | ||
2485 | #ifdef __ARCH_WANT_SYS_SOCKETCALL | 2508 | #ifdef __ARCH_WANT_SYS_SOCKETCALL |
2486 | /* Argument list sizes for sys_socketcall */ | 2509 | /* Argument list sizes for sys_socketcall */ |
@@ -2600,8 +2623,15 @@ SYSCALL_DEFINE2(socketcall, int, call, unsigned long __user *, args) | |||
2600 | a[2], true); | 2623 | a[2], true); |
2601 | break; | 2624 | break; |
2602 | case SYS_RECVMMSG: | 2625 | case SYS_RECVMMSG: |
2603 | err = do_sys_recvmmsg(a0, (struct mmsghdr __user *)a1, a[2], | 2626 | if (IS_ENABLED(CONFIG_64BIT) || !IS_ENABLED(CONFIG_64BIT_TIME)) |
2604 | a[3], (struct __kernel_timespec __user *)a[4]); | 2627 | err = __sys_recvmmsg(a0, (struct mmsghdr __user *)a1, |
2628 | a[2], a[3], | ||
2629 | (struct __kernel_timespec __user *)a[4], | ||
2630 | NULL); | ||
2631 | else | ||
2632 | err = __sys_recvmmsg(a0, (struct mmsghdr __user *)a1, | ||
2633 | a[2], a[3], NULL, | ||
2634 | (struct old_timespec32 __user *)a[4]); | ||
2605 | break; | 2635 | break; |
2606 | case SYS_ACCEPT4: | 2636 | case SYS_ACCEPT4: |
2607 | err = __sys_accept4(a0, (struct sockaddr __user *)a1, | 2637 | err = __sys_accept4(a0, (struct sockaddr __user *)a1, |