Signed-off-by: Arnd Bergmann arnd@arndb.de --- include/linux/compat_time.h | 3 +++ include/net/compat.h | 3 +++ include/uapi/linux/net.h | 1 + net/compat.c | 18 ++++++++++++------ net/socket.c | 36 ++++++++++++++++++++++++++++++++++-- 5 files changed, 53 insertions(+), 8 deletions(-)
diff --git a/include/linux/compat_time.h b/include/linux/compat_time.h index dada7bd49f58..6d129a7f3843 100644 --- a/include/linux/compat_time.h +++ b/include/linux/compat_time.h @@ -244,6 +244,9 @@ struct compat_mmsghdr; asmlinkage long compat_sys_recvmmsg(int fd, struct compat_mmsghdr __user *mmsg, unsigned vlen, unsigned int flags, struct compat_timespec __user *timeout); +asmlinkage long compat_sys_recvmmsg64(int fd, struct compat_mmsghdr __user *mmsg, + unsigned vlen, unsigned int flags, + struct __kernel_timespec __user *timeout); asmlinkage long compat_sys_nanosleep(struct compat_timespec __user *rqtp, struct compat_timespec __user *rmtp); asmlinkage long compat_sys_getitimer(int which, diff --git a/include/net/compat.h b/include/net/compat.h index 48103cf94e97..dbb5b6b949c5 100644 --- a/include/net/compat.h +++ b/include/net/compat.h @@ -51,6 +51,9 @@ asmlinkage long compat_sys_recvmsg(int, struct compat_msghdr __user *, asmlinkage long compat_sys_recvmmsg(int, struct compat_mmsghdr __user *, unsigned int, unsigned int, struct compat_timespec __user *); +asmlinkage long compat_sys_recvmmsg64(int, struct compat_mmsghdr __user *, + unsigned int, unsigned int, + struct __kernel_timespec __user *); asmlinkage long compat_sys_getsockopt(int, int, int, char __user *, int __user *); int put_cmsg_compat(struct msghdr*, int, int, int, void *); diff --git a/include/uapi/linux/net.h b/include/uapi/linux/net.h index 9457239ed219..7601df60f380 100644 --- a/include/uapi/linux/net.h +++ b/include/uapi/linux/net.h @@ -43,6 +43,7 @@ #define SYS_ACCEPT4 18 /* sys_accept4(2) */ #define SYS_RECVMMSG 19 /* sys_recvmmsg(2) */ #define SYS_SENDMMSG 20 /* sys_sendmmsg(2) */ +#define SYS_RECVMMSG64 21 /* sys_recvmmsg(2), time64_t */
typedef enum { SS_FREE = 0, /* not allocated */ diff --git a/net/compat.c b/net/compat.c index 5cfd26a0006f..15c326adbb6c 100644 --- a/net/compat.c +++ b/net/compat.c @@ -743,23 +743,23 @@ COMPAT_SYSCALL_DEFINE6(recvfrom, int, fd, void __user *, buf, compat_size_t, len return sys_recvfrom(fd, buf, len, flags | MSG_CMSG_COMPAT, addr, addrlen); }
-COMPAT_SYSCALL_DEFINE5(recvmmsg, int, fd, struct compat_mmsghdr __user *, mmsg, +COMPAT_SYSCALL_DEFINE5(recvmmsg64, int, fd, struct compat_mmsghdr __user *, mmsg, unsigned int, vlen, unsigned int, flags, - struct compat_timespec __user *, timeout) + struct __kernel_timespec __user *, timeout) { int datagrams; - struct timespec ktspec; + struct timespec64 timeout_sys;
if (timeout == NULL) return __sys_recvmmsg(fd, (struct mmsghdr __user *)mmsg, vlen, flags | MSG_CMSG_COMPAT, NULL);
- if (compat_get_timespec(&ktspec, timeout)) + if (get_timespec64(&timeout_sys, timeout)) return -EFAULT;
datagrams = __sys_recvmmsg(fd, (struct mmsghdr __user *)mmsg, vlen, - flags | MSG_CMSG_COMPAT, &ktspec); - if (datagrams > 0 && compat_put_timespec(&ktspec, timeout)) + flags | MSG_CMSG_COMPAT, &timeout_sys); + if (datagrams > 0 && put_timespec64(&timeout_sys, timeout)) datagrams = -EFAULT;
return datagrams; @@ -836,13 +836,19 @@ COMPAT_SYSCALL_DEFINE2(socketcall, int, call, u32 __user *, args) case SYS_RECVMSG: ret = compat_sys_recvmsg(a0, compat_ptr(a1), a[2]); break; +#ifdef CONFIG_COMPAT_TIME case SYS_RECVMMSG: ret = compat_sys_recvmmsg(a0, compat_ptr(a1), a[2], a[3], compat_ptr(a[4])); break; +#endif case SYS_ACCEPT4: ret = sys_accept4(a0, compat_ptr(a1), compat_ptr(a[2]), a[3]); break; + case SYS_RECVMMSG64: + ret = compat_sys_recvmmsg64(a0, compat_ptr(a1), a[2], a[3], + compat_ptr(a[4])); + break; default: ret = -EINVAL; break; diff --git a/net/socket.c b/net/socket.c index bfe50f60688b..632e0004c972 100644 --- a/net/socket.c +++ b/net/socket.c @@ -2197,6 +2197,9 @@ int __sys_recvmmsg(int fd, struct mmsghdr __user *mmsg, unsigned int vlen, entry = mmsg; compat_entry = (struct compat_mmsghdr __user *)mmsg;
+ if (!IS_ENABLED(CONFIG_COMPAT)) + flags &= ~MSG_CMSG_COMPAT; + while (datagrams < vlen) { /* * No need to ask LSM for more than the first datagram. @@ -2291,13 +2294,36 @@ SYSCALL_DEFINE5(recvmmsg, int, fd, struct mmsghdr __user *, mmsg,
datagrams = __sys_recvmmsg(fd, mmsg, vlen, flags, &timeout_sys);
- if (datagrams > 0 && - put_timespec64(&timeout_sys, timeout)) + if (datagrams > 0 && put_timespec64(&timeout_sys, timeout)) datagrams = -EFAULT;
return datagrams; }
+#ifdef CONFIG_COMPAT_TIME +COMPAT_SYSCALL_DEFINE5(recvmmsg, int, fd, struct compat_mmsghdr __user *, mmsg, + unsigned int, vlen, unsigned int, flags, + struct compat_timespec __user *, timeout) +{ + int datagrams; + struct timespec64 ktspec; + + if (timeout == NULL) + return __sys_recvmmsg(fd, (struct mmsghdr __user *)mmsg, vlen, + flags | MSG_CMSG_COMPAT, NULL); + + if (compat_get_timespec64(&ktspec, timeout)) + return -EFAULT; + + datagrams = __sys_recvmmsg(fd, (struct mmsghdr __user *)mmsg, vlen, + flags | MSG_CMSG_COMPAT, &ktspec); + if (datagrams > 0 && compat_put_timespec64(&ktspec, timeout)) + datagrams = -EFAULT; + + return datagrams; +} +#endif + #ifdef __ARCH_WANT_SYS_SOCKETCALL /* Argument list sizes for sys_socketcall */ #define AL(x) ((x) * sizeof(unsigned long)) @@ -2409,6 +2435,12 @@ SYSCALL_DEFINE2(socketcall, int, call, unsigned long __user *, args) err = sys_recvmsg(a0, (struct user_msghdr __user *)a1, a[2]); break; case SYS_RECVMMSG: +#if !defined(CONFIG_64BIT) && defined(CONFIG_COMPAT_TIME) + err = compat_sys_recvmmsg(a0, (struct mmsghdr __user *)a1, a[2], a[3], + (struct compat_timespec __user *)a[4]); + break; + case SYS_RECVMMSG64: +#endif err = sys_recvmmsg(a0, (struct mmsghdr __user *)a1, a[2], a[3], (struct __kernel_timespec __user *)a[4]); break;