summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorArnd Bergmann <arnd@arndb.de>2018-04-18 07:43:52 -0400
committerArnd Bergmann <arnd@arndb.de>2018-12-18 10:13:04 -0500
commite11d4284e2f4de5048c6d1787c82226f0a198292 (patch)
treebc52de794fd0fc90e2842a9d680239d6c3e9c215
parentbec2f7cbb73eadf5e1cc7d54ecb0980ede244257 (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.h3
-rw-r--r--include/linux/socket.h9
-rw-r--r--include/linux/syscalls.h3
-rw-r--r--kernel/sys_ni.c2
-rw-r--r--net/compat.c34
-rw-r--r--net/socket.c62
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,
893asmlinkage long compat_sys_rt_tgsigqueueinfo(compat_pid_t tgid, 893asmlinkage 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);
896asmlinkage long compat_sys_recvmmsg_time64(int fd, struct compat_mmsghdr __user *mmsg,
897 unsigned vlen, unsigned int flags,
898 struct __kernel_timespec __user *timeout);
896asmlinkage long compat_sys_recvmmsg(int fd, struct compat_mmsghdr __user *mmsg, 899asmlinkage 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 {
348extern int move_addr_to_kernel(void __user *uaddr, int ulen, struct sockaddr_storage *kaddr); 348extern int move_addr_to_kernel(void __user *uaddr, int ulen, struct sockaddr_storage *kaddr);
349extern int put_cmsg(struct msghdr*, int level, int type, int len, void *data); 349extern int put_cmsg(struct msghdr*, int level, int type, int len, void *data);
350 350
351struct timespec64; 351struct __kernel_timespec;
352struct 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);
358extern long __sys_sendmsg(int fd, struct user_msghdr __user *msg, 359extern 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);
360extern int __sys_recvmmsg(int fd, struct mmsghdr __user *mmsg, unsigned int vlen, 361extern 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);
362extern int __sys_sendmmsg(int fd, struct mmsghdr __user *mmsg, 365extern 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);
843asmlinkage long sys_recvmmsg(int fd, struct mmsghdr __user *msg, 843asmlinkage 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);
846asmlinkage long sys_recvmmsg_time32(int fd, struct mmsghdr __user *msg,
847 unsigned int vlen, unsigned flags,
848 struct old_timespec32 __user *timeout);
846 849
847asmlinkage long sys_wait4(pid_t pid, int __user *stat_addr, 850asmlinkage 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);
284COND_SYSCALL(perf_event_open); 284COND_SYSCALL(perf_event_open);
285COND_SYSCALL(accept4); 285COND_SYSCALL(accept4);
286COND_SYSCALL(recvmmsg); 286COND_SYSCALL(recvmmsg);
287COND_SYSCALL(recvmmsg_time32);
287COND_SYSCALL_COMPAT(recvmmsg); 288COND_SYSCALL_COMPAT(recvmmsg);
289COND_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
813static int __compat_sys_recvmmsg(int fd, struct compat_mmsghdr __user *mmsg, 813COMPAT_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
835COMPAT_SYSCALL_DEFINE5(recvmmsg, int, fd, struct compat_mmsghdr __user *, mmsg, 822COMPAT_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
842COMPAT_SYSCALL_DEFINE2(socketcall, int, call, u32 __user *, args) 831COMPAT_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
2344int __sys_recvmmsg(int fd, struct mmsghdr __user *mmsg, unsigned int vlen, 2344static 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
2454static int do_sys_recvmmsg(int fd, struct mmsghdr __user *mmsg, 2455int __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
2497SYSCALL_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,