From: Jiri Pirko <jpirko@redhat.com> Date: Wed, 24 Nov 2010 16:21:48 -0500 Subject: [net] limit sendto/recvfrom/iovec total length to INT_MAX Message-id: <20101124162148.GB4372@psychotron.redhat.com> Patchwork-id: 29597 O-Subject: [RHEL5.6 patch] BZ645872 CVE-2010-3859 net: Limit sendto()/recvfrom()/iovec total length to INT_MAX Bugzilla: 645872 CVE: CVE-2010-3859 RH-Acked-by: Neil Horman <nhorman@redhat.com> RH-Acked-by: David S. Miller <davem@redhat.com> RH-Acked-by: Thomas Graf <tgraf@redhat.com> BZ645872 https://bugzilla.redhat.com/show_bug.cgi?id=645872 Description: This helps protect us from overflow issues down in the individual protocol sendmsg/recvmsg handlers. Once we hit INT_MAX we truncate out the rest of the iovec by setting the iov_len members to zero. This works because: 1) For SOCK_STREAM and SOCK_SEQPACKET sockets, partial writes are allowed and the application will just continue with another write to send the rest of the data. 2) For datagram oriented sockets, where there must be a one-to-one correspondance between write() calls and packets on the wire, INT_MAX is going to be far larger than the packet size limit the protocol is going to check for and signal with -EMSGSIZE. Upstream: http://git.kernel.org/?p=linux/kernel/git/torvalds/linux-2.6.git;a=commitdiff;h=8acfe468b0384e834a303f08ebc4953d72fb690a http://git.kernel.org/?p=linux/kernel/git/torvalds/linux-2.6.git;a=commitdiff;h=253eacc070b114c2ec1f81b067d2fed7305467b0 Brew: https://brewweb.devel.redhat.com/taskinfo?taskID=2911903 Tested by bz-referenced reproducer Jirka Signed-off-by: Jiri Pirko <jpirko@redhat.com> Signed-off-by: Jarod Wilson <jarod@redhat.com> diff --git a/net/compat.c b/net/compat.c index abea845..8adb946 100644 --- a/net/compat.c +++ b/net/compat.c @@ -41,11 +41,13 @@ static inline int iov_from_user_compat_to_kern(struct iovec *kiov, compat_uptr_t buf; compat_size_t len; - if(get_user(len, &uiov32->iov_len) || - get_user(buf, &uiov32->iov_base)) { - tot_len = -EFAULT; - break; - } + if (get_user(len, &uiov32->iov_len) || + get_user(buf, &uiov32->iov_base)) + return -EFAULT; + + if (len > INT_MAX - tot_len) + len = INT_MAX - tot_len; + tot_len += len; kiov->iov_base = compat_ptr(buf); kiov->iov_len = (__kernel_size_t) len; diff --git a/net/core/iovec.c b/net/core/iovec.c index 65e4b56..d647b3e 100644 --- a/net/core/iovec.c +++ b/net/core/iovec.c @@ -61,14 +61,13 @@ int verify_iovec(struct msghdr *m, struct iovec *iov, char *address, int mode) err = 0; for (ct = 0; ct < m->msg_iovlen; ct++) { - err += iov[ct].iov_len; - /* - * Goal is not to verify user data, but to prevent returning - * negative value, which is interpreted as errno. - * Overflow is still possible, but it is harmless. - */ - if (err < 0) - return -EMSGSIZE; + size_t len = iov[ct].iov_len; + + if (len > INT_MAX - err) { + len = INT_MAX - err; + iov[ct].iov_len = len; + } + err += len; } return err; diff --git a/net/socket.c b/net/socket.c index 5589e76..aa0ecac 100644 --- a/net/socket.c +++ b/net/socket.c @@ -1627,6 +1627,8 @@ asmlinkage long sys_sendto(int fd, void __user * buff, size_t len, unsigned flag int fput_needed; struct file *sock_file; + if (len > INT_MAX) + len = INT_MAX; sock_file = fget_light(fd, &fput_needed); if (!sock_file) return -EBADF; @@ -1685,6 +1687,8 @@ asmlinkage long sys_recvfrom(int fd, void __user * ubuf, size_t size, unsigned f struct file *sock_file; int fput_needed; + if (size > INT_MAX) + size = INT_MAX; sock_file = fget_light(fd, &fput_needed); if (!sock_file) return -EBADF;