Sophie

Sophie

distrib > Scientific%20Linux > 5x > x86_64 > by-pkgid > 27922b4260f65d317aabda37e42bbbff > files > 2844

kernel-2.6.18-238.el5.src.rpm

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;