[linux] 01/01: Fix potential integer overflow issues in read/write paths

debian-kernel at lists.debian.org debian-kernel at lists.debian.org
Sun Feb 28 18:22:44 UTC 2016


This is an automated email from the git hooks/post-receive script.

benh pushed a commit to branch squeeze-security
in repository linux.

commit 958ce91476550792795484477ce3a28c6b5025e1
Author: Ben Hutchings <ben at decadent.org.uk>
Date:   Sun Feb 28 18:09:21 2016 +0000

    Fix potential integer overflow issues in read/write paths
    
    I identified these when analysing the more recent regression of AIO's
    length limiting.
---
 debian/changelog                                   |   3 +
 ...v-do-the-same-max_rw_count-truncation-tha.patch | 137 +++++++++++++++++++++
 ...o-use-the-proper-rw_verify_area-area-help.patch | 104 ++++++++++++++++
 debian/patches/series/48squeeze20                  |   2 +
 4 files changed, 246 insertions(+)

diff --git a/debian/changelog b/debian/changelog
index e858858..2609bb9 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -7,6 +7,9 @@ linux-2.6 (2.6.32-48squeeze20) UNRELEASED; urgency=medium
   * ALSA: usb-audio: avoid freeing umidi object twice (CVE-2016-2384)
   * af_unix: Guard against other == sk in unix_dgram_sendmsg
     (regression in 2.6.32-48squeeze17)
+  * Fix potential integer overflow issues in read/write paths:
+    - readv/writev: do the same MAX_RW_COUNT truncation that read/write does
+    - vfs: make AIO use the proper rw_verify_area() area helpers
 
  -- Ben Hutchings <ben at decadent.org.uk>  Sat, 13 Feb 2016 18:55:35 +0000
 
diff --git a/debian/patches/bugfix/all/readv-writev-do-the-same-max_rw_count-truncation-tha.patch b/debian/patches/bugfix/all/readv-writev-do-the-same-max_rw_count-truncation-tha.patch
new file mode 100644
index 0000000..a1677e2
--- /dev/null
+++ b/debian/patches/bugfix/all/readv-writev-do-the-same-max_rw_count-truncation-tha.patch
@@ -0,0 +1,137 @@
+From: Linus Torvalds <torvalds at linux-foundation.org>
+Date: Fri, 29 Oct 2010 10:36:49 -0700
+Subject: readv/writev: do the same MAX_RW_COUNT truncation that read/write
+ does
+Origin: https://git.kernel.org/linus/435f49a518c78eec8e2edbbadd912737246cbe20
+
+We used to protect against overflow, but rather than return an error, do
+what read/write does, namely to limit the total size to MAX_RW_COUNT.
+This is not only more consistent, but it also means that any broken
+low-level read/write routine that still keeps counts in 'int' can't
+break.
+
+Signed-off-by: Linus Torvalds <torvalds at linux-foundation.org>
+[bwh: Backported to 2.6.32: compat_rw_copy_check_uvector() doesn't exist here]
+---
+--- a/fs/read_write.c
++++ b/fs/read_write.c
+@@ -210,8 +210,6 @@ bad:
+  * them to something that fits in "int" so that others
+  * won't have to do range checks all the time.
+  */
+-#define MAX_RW_COUNT (INT_MAX & PAGE_CACHE_MASK)
+-
+ int rw_verify_area(int read_write, struct file *file, loff_t *ppos, size_t count)
+ {
+ 	struct inode *inode;
+@@ -546,65 +544,71 @@ ssize_t rw_copy_check_uvector(int type, const struct iovec __user * uvector,
+ 			      unsigned long nr_segs, unsigned long fast_segs,
+ 			      struct iovec *fast_pointer,
+ 			      struct iovec **ret_pointer)
+-  {
++{
+ 	unsigned long seg;
+-  	ssize_t ret;
++	ssize_t ret;
+ 	struct iovec *iov = fast_pointer;
+ 
+-  	/*
+-  	 * SuS says "The readv() function *may* fail if the iovcnt argument
+-  	 * was less than or equal to 0, or greater than {IOV_MAX}.  Linux has
+-  	 * traditionally returned zero for zero segments, so...
+-  	 */
++	/*
++	 * SuS says "The readv() function *may* fail if the iovcnt argument
++	 * was less than or equal to 0, or greater than {IOV_MAX}.  Linux has
++	 * traditionally returned zero for zero segments, so...
++	 */
+ 	if (nr_segs == 0) {
+ 		ret = 0;
+-  		goto out;
++		goto out;
+ 	}
+ 
+-  	/*
+-  	 * First get the "struct iovec" from user memory and
+-  	 * verify all the pointers
+-  	 */
++	/*
++	 * First get the "struct iovec" from user memory and
++	 * verify all the pointers
++	 */
+ 	if (nr_segs > UIO_MAXIOV) {
+ 		ret = -EINVAL;
+-  		goto out;
++		goto out;
+ 	}
+ 	if (nr_segs > fast_segs) {
+-  		iov = kmalloc(nr_segs*sizeof(struct iovec), GFP_KERNEL);
++		iov = kmalloc(nr_segs*sizeof(struct iovec), GFP_KERNEL);
+ 		if (iov == NULL) {
+ 			ret = -ENOMEM;
+-  			goto out;
++			goto out;
+ 		}
+-  	}
++	}
+ 	if (copy_from_user(iov, uvector, nr_segs*sizeof(*uvector))) {
+ 		ret = -EFAULT;
+-  		goto out;
++		goto out;
+ 	}
+ 
+-  	/*
++	/*
+ 	 * According to the Single Unix Specification we should return EINVAL
+ 	 * if an element length is < 0 when cast to ssize_t or if the
+ 	 * total length would overflow the ssize_t return value of the
+ 	 * system call.
+-  	 */
++	 *
++	 * Linux caps all read/write calls to MAX_RW_COUNT, and avoids the
++	 * overflow case.
++	 */
+ 	ret = 0;
+-  	for (seg = 0; seg < nr_segs; seg++) {
+-  		void __user *buf = iov[seg].iov_base;
+-  		ssize_t len = (ssize_t)iov[seg].iov_len;
++	for (seg = 0; seg < nr_segs; seg++) {
++		void __user *buf = iov[seg].iov_base;
++		ssize_t len = (ssize_t)iov[seg].iov_len;
+ 
+ 		/* see if we we're about to use an invalid len or if
+ 		 * it's about to overflow ssize_t */
+-		if (len < 0 || (ret + len < ret)) {
++		if (len < 0) {
+ 			ret = -EINVAL;
+-  			goto out;
++			goto out;
+ 		}
+ 		if (unlikely(!access_ok(vrfy_dir(type), buf, len))) {
+ 			ret = -EFAULT;
+-  			goto out;
++			goto out;
++		}
++		if (len > MAX_RW_COUNT - ret) {
++			len = MAX_RW_COUNT - ret;
++			iov[seg].iov_len = len;
+ 		}
+-
+ 		ret += len;
+-  	}
++	}
+ out:
+ 	*ret_pointer = iov;
+ 	return ret;
+diff --git a/include/linux/fs.h b/include/linux/fs.h
+index 4d07902bc50c..7b7b507ffa1c 100644
+--- a/include/linux/fs.h
++++ b/include/linux/fs.h
+@@ -1820,6 +1820,7 @@ extern int current_umask(void);
+ /* /sys/fs */
+ extern struct kobject *fs_kobj;
+ 
++#define MAX_RW_COUNT (INT_MAX & PAGE_CACHE_MASK)
+ extern int rw_verify_area(int, struct file *, loff_t *, size_t);
+ 
+ #define FLOCK_VERIFY_READ  1
diff --git a/debian/patches/bugfix/all/vfs-make-aio-use-the-proper-rw_verify_area-area-help.patch b/debian/patches/bugfix/all/vfs-make-aio-use-the-proper-rw_verify_area-area-help.patch
new file mode 100644
index 0000000..a8f9253
--- /dev/null
+++ b/debian/patches/bugfix/all/vfs-make-aio-use-the-proper-rw_verify_area-area-help.patch
@@ -0,0 +1,104 @@
+From: Linus Torvalds <torvalds at linux-foundation.org>
+Date: Mon, 21 May 2012 16:06:20 -0700
+Subject: vfs: make AIO use the proper rw_verify_area() area helpers
+Origin: https://git.kernel.org/linus/a70b52ec1aaeaf60f4739edb1b422827cb6f3893
+
+We had for some reason overlooked the AIO interface, and it didn't use
+the proper rw_verify_area() helper function that checks (for example)
+mandatory locking on the file, and that the size of the access doesn't
+cause us to overflow the provided offset limits etc.
+
+Instead, AIO did just the security_file_permission() thing (that
+rw_verify_area() also does) directly.
+
+This fixes it to do all the proper helper functions, which not only
+means that now mandatory file locking works with AIO too, we can
+actually remove lines of code.
+
+Reported-by: Manish Honap <manish_honap_vit at yahoo.co.in>
+Cc: stable at vger.kernel.org
+Signed-off-by: Linus Torvalds <torvalds at linux-foundation.org>
+[bwh: Backported to 3.2: adjust context]
+---
+ fs/aio.c | 30 ++++++++++++++----------------
+ 1 file changed, 14 insertions(+), 16 deletions(-)
+
+diff --git a/fs/aio.c b/fs/aio.c
+index 67a6db3e1b6f..e7f2fad7b4ce 100644
+--- a/fs/aio.c
++++ b/fs/aio.c
+@@ -1389,6 +1389,10 @@ static ssize_t aio_setup_vectored_rw(int type, struct kiocb *kiocb, bool compat)
+ 	if (ret < 0)
+ 		goto out;
+ 
++	ret = rw_verify_area(type, kiocb->ki_filp, &kiocb->ki_pos, ret);
++	if (ret < 0)
++		goto out;
++
+ 	kiocb->ki_nr_segs = kiocb->ki_nbytes;
+ 	kiocb->ki_cur_seg = 0;
+ 	/* ki_nbytes/left now reflect bytes instead of segs */
+@@ -1400,11 +1404,17 @@ out:
+ 	return ret;
+ }
+ 
+-static ssize_t aio_setup_single_vector(struct kiocb *kiocb)
++static ssize_t aio_setup_single_vector(int type, struct file * file, struct kiocb *kiocb)
+ {
++	int bytes;
++
++	bytes = rw_verify_area(type, file, &kiocb->ki_pos, kiocb->ki_left);
++	if (bytes < 0)
++		return bytes;
++
+ 	kiocb->ki_iovec = &kiocb->ki_inline_vec;
+ 	kiocb->ki_iovec->iov_base = kiocb->ki_buf;
+-	kiocb->ki_iovec->iov_len = kiocb->ki_left;
++	kiocb->ki_iovec->iov_len = bytes;
+ 	kiocb->ki_nr_segs = 1;
+ 	kiocb->ki_cur_seg = 0;
+ 	return 0;
+@@ -1429,10 +1439,7 @@ static ssize_t aio_setup_iocb(struct kiocb *kiocb, bool compat)
+ 		if (unlikely(!access_ok(VERIFY_WRITE, kiocb->ki_buf,
+ 			kiocb->ki_left)))
+ 			break;
+-		ret = security_file_permission(file, MAY_READ);
+-		if (unlikely(ret))
+-			break;
+-		ret = aio_setup_single_vector(kiocb);
++		ret = aio_setup_single_vector(READ, file, kiocb);
+ 		if (ret)
+ 			break;
+ 		ret = -EINVAL;
+@@ -1447,10 +1454,7 @@ static ssize_t aio_setup_iocb(struct kiocb *kiocb, bool compat)
+ 		if (unlikely(!access_ok(VERIFY_READ, kiocb->ki_buf,
+ 			kiocb->ki_left)))
+ 			break;
+-		ret = security_file_permission(file, MAY_WRITE);
+-		if (unlikely(ret))
+-			break;
+-		ret = aio_setup_single_vector(kiocb);
++		ret = aio_setup_single_vector(WRITE, file, kiocb);
+ 		if (ret)
+ 			break;
+ 		ret = -EINVAL;
+@@ -1461,9 +1465,6 @@ static ssize_t aio_setup_iocb(struct kiocb *kiocb, bool compat)
+ 		ret = -EBADF;
+ 		if (unlikely(!(file->f_mode & FMODE_READ)))
+ 			break;
+-		ret = security_file_permission(file, MAY_READ);
+-		if (unlikely(ret))
+-			break;
+ 		ret = aio_setup_vectored_rw(READ, kiocb);
+ 		if (ret)
+ 			break;
+@@ -1475,9 +1476,6 @@ static ssize_t aio_setup_iocb(struct kiocb *kiocb, bool compat)
+ 		ret = -EBADF;
+ 		if (unlikely(!(file->f_mode & FMODE_WRITE)))
+ 			break;
+-		ret = security_file_permission(file, MAY_WRITE);
+-		if (unlikely(ret))
+-			break;
+ 		ret = aio_setup_vectored_rw(WRITE, kiocb);
+ 		if (ret)
+ 			break;
diff --git a/debian/patches/series/48squeeze20 b/debian/patches/series/48squeeze20
index 92985e7..5e1bd7b 100644
--- a/debian/patches/series/48squeeze20
+++ b/debian/patches/series/48squeeze20
@@ -3,3 +3,5 @@
 + bugfix/all/iw_cxgb3-Fix-incorrectly-returning-error-on-success.patch
 + bugfix/all/alsa-usb-audio-avoid-freeing-umidi-object-twice.patch
 + bugfix/all/af_unix-guard-against-other-sk-in-unix_dgram_sendmsg.patch
++ bugfix/all/readv-writev-do-the-same-max_rw_count-truncation-tha.patch
++ bugfix/all/vfs-make-aio-use-the-proper-rw_verify_area-area-help.patch

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/kernel/linux.git



More information about the Kernel-svn-changes mailing list