aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPaX Team <pageexec@freemail.hu>2014-01-30 19:59:25 -0500
committerLinus Torvalds <torvalds@linux-foundation.org>2014-01-30 21:44:13 -0500
commit2def2ef2ae5f3990aabdbe8a755911902707d268 (patch)
tree311170d9f7b737cf8fc220bc85b502753476f6ac
parent12f2bbd609006f983c1c99d240cf61e6e829a14c (diff)
x86, x32: Correct invalid use of user timespec in the kernel
The x32 case for the recvmsg() timout handling is broken: asmlinkage long compat_sys_recvmmsg(int fd, struct compat_mmsghdr __user *mmsg, unsigned int vlen, unsigned int flags, struct compat_timespec __user *timeout) { int datagrams; struct timespec ktspec; if (flags & MSG_CMSG_COMPAT) return -EINVAL; if (COMPAT_USE_64BIT_TIME) return __sys_recvmmsg(fd, (struct mmsghdr __user *)mmsg, vlen, flags | MSG_CMSG_COMPAT, (struct timespec *) timeout); ... The timeout pointer parameter is provided by userland (hence the __user annotation) but for x32 syscalls it's simply cast to a kernel pointer and is passed to __sys_recvmmsg which will eventually directly dereference it for both reading and writing. Other callers to __sys_recvmmsg properly copy from userland to the kernel first. The bug was introduced by commit ee4fa23c4bfc ("compat: Use COMPAT_USE_64BIT_TIME in net/compat.c") and should affect all kernels since 3.4 (and perhaps vendor kernels if they backported x32 support along with this code). Note that CONFIG_X86_X32_ABI gets enabled at build time and only if CONFIG_X86_X32 is enabled and ld can build x32 executables. Other uses of COMPAT_USE_64BIT_TIME seem fine. This addresses CVE-2014-0038. Signed-off-by: PaX Team <pageexec@freemail.hu> Signed-off-by: H. Peter Anvin <hpa@linux.intel.com> Cc: <stable@vger.kernel.org> # v3.4+ Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
-rw-r--r--net/compat.c9
1 files changed, 2 insertions, 7 deletions
diff --git a/net/compat.c b/net/compat.c
index dd32e34c1e2c..f50161fb812e 100644
--- a/net/compat.c
+++ b/net/compat.c
@@ -780,21 +780,16 @@ asmlinkage long compat_sys_recvmmsg(int fd, struct compat_mmsghdr __user *mmsg,
780 if (flags & MSG_CMSG_COMPAT) 780 if (flags & MSG_CMSG_COMPAT)
781 return -EINVAL; 781 return -EINVAL;
782 782
783 if (COMPAT_USE_64BIT_TIME)
784 return __sys_recvmmsg(fd, (struct mmsghdr __user *)mmsg, vlen,
785 flags | MSG_CMSG_COMPAT,
786 (struct timespec *) timeout);
787
788 if (timeout == NULL) 783 if (timeout == NULL)
789 return __sys_recvmmsg(fd, (struct mmsghdr __user *)mmsg, vlen, 784 return __sys_recvmmsg(fd, (struct mmsghdr __user *)mmsg, vlen,
790 flags | MSG_CMSG_COMPAT, NULL); 785 flags | MSG_CMSG_COMPAT, NULL);
791 786
792 if (get_compat_timespec(&ktspec, timeout)) 787 if (compat_get_timespec(&ktspec, timeout))
793 return -EFAULT; 788 return -EFAULT;
794 789
795 datagrams = __sys_recvmmsg(fd, (struct mmsghdr __user *)mmsg, vlen, 790 datagrams = __sys_recvmmsg(fd, (struct mmsghdr __user *)mmsg, vlen,
796 flags | MSG_CMSG_COMPAT, &ktspec); 791 flags | MSG_CMSG_COMPAT, &ktspec);
797 if (datagrams > 0 && put_compat_timespec(&ktspec, timeout)) 792 if (datagrams > 0 && compat_put_timespec(&ktspec, timeout))
798 datagrams = -EFAULT; 793 datagrams = -EFAULT;
799 794
800 return datagrams; 795 return datagrams;