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>
This commit is contained in:
parent
bec2f7cbb7
commit
e11d4284e2
|
@ -893,6 +893,9 @@ asmlinkage long compat_sys_move_pages(pid_t pid, compat_ulong_t nr_pages,
|
||||||
asmlinkage long compat_sys_rt_tgsigqueueinfo(compat_pid_t tgid,
|
asmlinkage long compat_sys_rt_tgsigqueueinfo(compat_pid_t tgid,
|
||||||
compat_pid_t pid, int sig,
|
compat_pid_t pid, int sig,
|
||||||
struct compat_siginfo __user *uinfo);
|
struct compat_siginfo __user *uinfo);
|
||||||
|
asmlinkage long compat_sys_recvmmsg_time64(int fd, struct compat_mmsghdr __user *mmsg,
|
||||||
|
unsigned vlen, unsigned int flags,
|
||||||
|
struct __kernel_timespec __user *timeout);
|
||||||
asmlinkage long compat_sys_recvmmsg(int fd, struct compat_mmsghdr __user *mmsg,
|
asmlinkage long compat_sys_recvmmsg(int fd, struct compat_mmsghdr __user *mmsg,
|
||||||
unsigned vlen, unsigned int flags,
|
unsigned vlen, unsigned int flags,
|
||||||
struct old_timespec32 __user *timeout);
|
struct old_timespec32 __user *timeout);
|
||||||
|
|
|
@ -348,7 +348,8 @@ struct ucred {
|
||||||
extern int move_addr_to_kernel(void __user *uaddr, int ulen, struct sockaddr_storage *kaddr);
|
extern int move_addr_to_kernel(void __user *uaddr, int ulen, struct sockaddr_storage *kaddr);
|
||||||
extern int put_cmsg(struct msghdr*, int level, int type, int len, void *data);
|
extern int put_cmsg(struct msghdr*, int level, int type, int len, void *data);
|
||||||
|
|
||||||
struct timespec64;
|
struct __kernel_timespec;
|
||||||
|
struct old_timespec32;
|
||||||
|
|
||||||
/* The __sys_...msg variants allow MSG_CMSG_COMPAT iff
|
/* The __sys_...msg variants allow MSG_CMSG_COMPAT iff
|
||||||
* forbid_cmsg_compat==false
|
* forbid_cmsg_compat==false
|
||||||
|
@ -357,8 +358,10 @@ extern long __sys_recvmsg(int fd, struct user_msghdr __user *msg,
|
||||||
unsigned int flags, bool forbid_cmsg_compat);
|
unsigned int flags, bool forbid_cmsg_compat);
|
||||||
extern long __sys_sendmsg(int fd, struct user_msghdr __user *msg,
|
extern long __sys_sendmsg(int fd, struct user_msghdr __user *msg,
|
||||||
unsigned int flags, bool forbid_cmsg_compat);
|
unsigned int flags, bool forbid_cmsg_compat);
|
||||||
extern int __sys_recvmmsg(int fd, struct mmsghdr __user *mmsg, unsigned int vlen,
|
extern int __sys_recvmmsg(int fd, struct mmsghdr __user *mmsg,
|
||||||
unsigned int flags, struct timespec64 *timeout);
|
unsigned int vlen, unsigned int flags,
|
||||||
|
struct __kernel_timespec __user *timeout,
|
||||||
|
struct old_timespec32 __user *timeout32);
|
||||||
extern int __sys_sendmmsg(int fd, struct mmsghdr __user *mmsg,
|
extern int __sys_sendmmsg(int fd, struct mmsghdr __user *mmsg,
|
||||||
unsigned int vlen, unsigned int flags,
|
unsigned int vlen, unsigned int flags,
|
||||||
bool forbid_cmsg_compat);
|
bool forbid_cmsg_compat);
|
||||||
|
|
|
@ -843,6 +843,9 @@ asmlinkage long sys_accept4(int, struct sockaddr __user *, int __user *, int);
|
||||||
asmlinkage long sys_recvmmsg(int fd, struct mmsghdr __user *msg,
|
asmlinkage long sys_recvmmsg(int fd, struct mmsghdr __user *msg,
|
||||||
unsigned int vlen, unsigned flags,
|
unsigned int vlen, unsigned flags,
|
||||||
struct __kernel_timespec __user *timeout);
|
struct __kernel_timespec __user *timeout);
|
||||||
|
asmlinkage long sys_recvmmsg_time32(int fd, struct mmsghdr __user *msg,
|
||||||
|
unsigned int vlen, unsigned flags,
|
||||||
|
struct old_timespec32 __user *timeout);
|
||||||
|
|
||||||
asmlinkage long sys_wait4(pid_t pid, int __user *stat_addr,
|
asmlinkage long sys_wait4(pid_t pid, int __user *stat_addr,
|
||||||
int options, struct rusage __user *ru);
|
int options, struct rusage __user *ru);
|
||||||
|
|
|
@ -284,7 +284,9 @@ COND_SYSCALL_COMPAT(move_pages);
|
||||||
COND_SYSCALL(perf_event_open);
|
COND_SYSCALL(perf_event_open);
|
||||||
COND_SYSCALL(accept4);
|
COND_SYSCALL(accept4);
|
||||||
COND_SYSCALL(recvmmsg);
|
COND_SYSCALL(recvmmsg);
|
||||||
|
COND_SYSCALL(recvmmsg_time32);
|
||||||
COND_SYSCALL_COMPAT(recvmmsg);
|
COND_SYSCALL_COMPAT(recvmmsg);
|
||||||
|
COND_SYSCALL_COMPAT(recvmmsg_time64);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Architecture specific syscalls: see further below
|
* Architecture specific syscalls: see further below
|
||||||
|
|
34
net/compat.c
34
net/compat.c
|
@ -810,34 +810,23 @@ COMPAT_SYSCALL_DEFINE6(recvfrom, int, fd, void __user *, buf, compat_size_t, len
|
||||||
return __compat_sys_recvfrom(fd, buf, len, flags, addr, addrlen);
|
return __compat_sys_recvfrom(fd, buf, len, flags, addr, addrlen);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int __compat_sys_recvmmsg(int fd, struct compat_mmsghdr __user *mmsg,
|
COMPAT_SYSCALL_DEFINE5(recvmmsg_time64, int, fd, struct compat_mmsghdr __user *, mmsg,
|
||||||
unsigned int vlen, unsigned int flags,
|
unsigned int, vlen, unsigned int, flags,
|
||||||
struct old_timespec32 __user *timeout)
|
struct __kernel_timespec __user *, timeout)
|
||||||
{
|
{
|
||||||
int datagrams;
|
return __sys_recvmmsg(fd, (struct mmsghdr __user *)mmsg, vlen,
|
||||||
struct timespec64 ktspec;
|
flags | MSG_CMSG_COMPAT, timeout, NULL);
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef CONFIG_COMPAT_32BIT_TIME
|
||||||
COMPAT_SYSCALL_DEFINE5(recvmmsg, int, fd, struct compat_mmsghdr __user *, mmsg,
|
COMPAT_SYSCALL_DEFINE5(recvmmsg, int, fd, struct compat_mmsghdr __user *, mmsg,
|
||||||
unsigned int, vlen, unsigned int, flags,
|
unsigned int, vlen, unsigned int, flags,
|
||||||
struct old_timespec32 __user *, timeout)
|
struct old_timespec32 __user *, timeout)
|
||||||
{
|
{
|
||||||
return __compat_sys_recvmmsg(fd, mmsg, vlen, flags, timeout);
|
return __sys_recvmmsg(fd, (struct mmsghdr __user *)mmsg, vlen,
|
||||||
|
flags | MSG_CMSG_COMPAT, NULL, timeout);
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
COMPAT_SYSCALL_DEFINE2(socketcall, int, call, u32 __user *, args)
|
COMPAT_SYSCALL_DEFINE2(socketcall, int, call, u32 __user *, args)
|
||||||
{
|
{
|
||||||
|
@ -925,8 +914,9 @@ COMPAT_SYSCALL_DEFINE2(socketcall, int, call, u32 __user *, args)
|
||||||
ret = __compat_sys_recvmsg(a0, compat_ptr(a1), a[2]);
|
ret = __compat_sys_recvmsg(a0, compat_ptr(a1), a[2]);
|
||||||
break;
|
break;
|
||||||
case SYS_RECVMMSG:
|
case SYS_RECVMMSG:
|
||||||
ret = __compat_sys_recvmmsg(a0, compat_ptr(a1), a[2], a[3],
|
ret = __sys_recvmmsg(a0, compat_ptr(a1), a[2],
|
||||||
compat_ptr(a[4]));
|
a[3] | MSG_CMSG_COMPAT, NULL,
|
||||||
|
compat_ptr(a[4]));
|
||||||
break;
|
break;
|
||||||
case SYS_ACCEPT4:
|
case SYS_ACCEPT4:
|
||||||
ret = __sys_accept4(a0, compat_ptr(a1), compat_ptr(a[2]), a[3]);
|
ret = __sys_accept4(a0, compat_ptr(a1), compat_ptr(a[2]), a[3]);
|
||||||
|
|
64
net/socket.c
64
net/socket.c
|
@ -2341,8 +2341,9 @@ SYSCALL_DEFINE3(recvmsg, int, fd, struct user_msghdr __user *, msg,
|
||||||
* Linux recvmmsg interface
|
* Linux recvmmsg interface
|
||||||
*/
|
*/
|
||||||
|
|
||||||
int __sys_recvmmsg(int fd, struct mmsghdr __user *mmsg, unsigned int vlen,
|
static int do_recvmmsg(int fd, struct mmsghdr __user *mmsg,
|
||||||
unsigned int flags, struct timespec64 *timeout)
|
unsigned int vlen, unsigned int flags,
|
||||||
|
struct timespec64 *timeout)
|
||||||
{
|
{
|
||||||
int fput_needed, err, datagrams;
|
int fput_needed, err, datagrams;
|
||||||
struct socket *sock;
|
struct socket *sock;
|
||||||
|
@ -2451,25 +2452,32 @@ out_put:
|
||||||
return datagrams;
|
return datagrams;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int do_sys_recvmmsg(int fd, struct mmsghdr __user *mmsg,
|
int __sys_recvmmsg(int fd, struct mmsghdr __user *mmsg,
|
||||||
unsigned int vlen, unsigned int flags,
|
unsigned int vlen, unsigned int flags,
|
||||||
struct __kernel_timespec __user *timeout)
|
struct __kernel_timespec __user *timeout,
|
||||||
|
struct old_timespec32 __user *timeout32)
|
||||||
{
|
{
|
||||||
int datagrams;
|
int datagrams;
|
||||||
struct timespec64 timeout_sys;
|
struct timespec64 timeout_sys;
|
||||||
|
|
||||||
if (flags & MSG_CMSG_COMPAT)
|
if (timeout && get_timespec64(&timeout_sys, timeout))
|
||||||
return -EINVAL;
|
|
||||||
|
|
||||||
if (!timeout)
|
|
||||||
return __sys_recvmmsg(fd, mmsg, vlen, flags, NULL);
|
|
||||||
|
|
||||||
if (get_timespec64(&timeout_sys, timeout))
|
|
||||||
return -EFAULT;
|
return -EFAULT;
|
||||||
|
|
||||||
datagrams = __sys_recvmmsg(fd, mmsg, vlen, flags, &timeout_sys);
|
if (timeout32 && get_old_timespec32(&timeout_sys, timeout32))
|
||||||
|
return -EFAULT;
|
||||||
|
|
||||||
if (datagrams > 0 && put_timespec64(&timeout_sys, timeout))
|
if (!timeout && !timeout32)
|
||||||
|
return do_recvmmsg(fd, mmsg, vlen, flags, NULL);
|
||||||
|
|
||||||
|
datagrams = do_recvmmsg(fd, mmsg, vlen, flags, &timeout_sys);
|
||||||
|
|
||||||
|
if (datagrams <= 0)
|
||||||
|
return datagrams;
|
||||||
|
|
||||||
|
if (timeout && put_timespec64(&timeout_sys, timeout))
|
||||||
|
datagrams = -EFAULT;
|
||||||
|
|
||||||
|
if (timeout32 && put_old_timespec32(&timeout_sys, timeout32))
|
||||||
datagrams = -EFAULT;
|
datagrams = -EFAULT;
|
||||||
|
|
||||||
return datagrams;
|
return datagrams;
|
||||||
|
@ -2479,9 +2487,24 @@ SYSCALL_DEFINE5(recvmmsg, int, fd, struct mmsghdr __user *, mmsg,
|
||||||
unsigned int, vlen, unsigned int, flags,
|
unsigned int, vlen, unsigned int, flags,
|
||||||
struct __kernel_timespec __user *, timeout)
|
struct __kernel_timespec __user *, timeout)
|
||||||
{
|
{
|
||||||
return do_sys_recvmmsg(fd, mmsg, vlen, flags, timeout);
|
if (flags & MSG_CMSG_COMPAT)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
return __sys_recvmmsg(fd, mmsg, vlen, flags, timeout, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef CONFIG_COMPAT_32BIT_TIME
|
||||||
|
SYSCALL_DEFINE5(recvmmsg_time32, int, fd, struct mmsghdr __user *, mmsg,
|
||||||
|
unsigned int, vlen, unsigned int, flags,
|
||||||
|
struct old_timespec32 __user *, timeout)
|
||||||
|
{
|
||||||
|
if (flags & MSG_CMSG_COMPAT)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
return __sys_recvmmsg(fd, mmsg, vlen, flags, NULL, timeout);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef __ARCH_WANT_SYS_SOCKETCALL
|
#ifdef __ARCH_WANT_SYS_SOCKETCALL
|
||||||
/* Argument list sizes for sys_socketcall */
|
/* Argument list sizes for sys_socketcall */
|
||||||
#define AL(x) ((x) * sizeof(unsigned long))
|
#define AL(x) ((x) * sizeof(unsigned long))
|
||||||
|
@ -2600,8 +2623,15 @@ SYSCALL_DEFINE2(socketcall, int, call, unsigned long __user *, args)
|
||||||
a[2], true);
|
a[2], true);
|
||||||
break;
|
break;
|
||||||
case SYS_RECVMMSG:
|
case SYS_RECVMMSG:
|
||||||
err = do_sys_recvmmsg(a0, (struct mmsghdr __user *)a1, a[2],
|
if (IS_ENABLED(CONFIG_64BIT) || !IS_ENABLED(CONFIG_64BIT_TIME))
|
||||||
a[3], (struct __kernel_timespec __user *)a[4]);
|
err = __sys_recvmmsg(a0, (struct mmsghdr __user *)a1,
|
||||||
|
a[2], a[3],
|
||||||
|
(struct __kernel_timespec __user *)a[4],
|
||||||
|
NULL);
|
||||||
|
else
|
||||||
|
err = __sys_recvmmsg(a0, (struct mmsghdr __user *)a1,
|
||||||
|
a[2], a[3], NULL,
|
||||||
|
(struct old_timespec32 __user *)a[4]);
|
||||||
break;
|
break;
|
||||||
case SYS_ACCEPT4:
|
case SYS_ACCEPT4:
|
||||||
err = __sys_accept4(a0, (struct sockaddr __user *)a1,
|
err = __sys_accept4(a0, (struct sockaddr __user *)a1,
|
||||||
|
|
Loading…
Reference in New Issue