aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--fs/compat.c12
-rw-r--r--fs/read_write.c62
-rw-r--r--include/linux/fs.h1
3 files changed, 40 insertions, 35 deletions
diff --git a/fs/compat.c b/fs/compat.c
index 52cfeb61da77..ff66c0d7583d 100644
--- a/fs/compat.c
+++ b/fs/compat.c
@@ -606,14 +606,14 @@ ssize_t compat_rw_copy_check_uvector(int type,
606 /* 606 /*
607 * Single unix specification: 607 * Single unix specification:
608 * We should -EINVAL if an element length is not >= 0 and fitting an 608 * We should -EINVAL if an element length is not >= 0 and fitting an
609 * ssize_t. The total length is fitting an ssize_t 609 * ssize_t.
610 * 610 *
611 * Be careful here because iov_len is a size_t not an ssize_t 611 * In Linux, the total length is limited to MAX_RW_COUNT, there is
612 * no overflow possibility.
612 */ 613 */
613 tot_len = 0; 614 tot_len = 0;
614 ret = -EINVAL; 615 ret = -EINVAL;
615 for (seg = 0; seg < nr_segs; seg++) { 616 for (seg = 0; seg < nr_segs; seg++) {
616 compat_ssize_t tmp = tot_len;
617 compat_uptr_t buf; 617 compat_uptr_t buf;
618 compat_ssize_t len; 618 compat_ssize_t len;
619 619
@@ -624,13 +624,13 @@ ssize_t compat_rw_copy_check_uvector(int type,
624 } 624 }
625 if (len < 0) /* size_t not fitting in compat_ssize_t .. */ 625 if (len < 0) /* size_t not fitting in compat_ssize_t .. */
626 goto out; 626 goto out;
627 tot_len += len;
628 if (tot_len < tmp) /* maths overflow on the compat_ssize_t */
629 goto out;
630 if (!access_ok(vrfy_dir(type), compat_ptr(buf), len)) { 627 if (!access_ok(vrfy_dir(type), compat_ptr(buf), len)) {
631 ret = -EFAULT; 628 ret = -EFAULT;
632 goto out; 629 goto out;
633 } 630 }
631 if (len > MAX_RW_COUNT - tot_len)
632 len = MAX_RW_COUNT - tot_len;
633 tot_len += len;
634 iov->iov_base = compat_ptr(buf); 634 iov->iov_base = compat_ptr(buf);
635 iov->iov_len = (compat_size_t) len; 635 iov->iov_len = (compat_size_t) len;
636 uvector++; 636 uvector++;
diff --git a/fs/read_write.c b/fs/read_write.c
index 9cd9d148105d..431a0ed610c8 100644
--- a/fs/read_write.c
+++ b/fs/read_write.c
@@ -243,8 +243,6 @@ bad:
243 * them to something that fits in "int" so that others 243 * them to something that fits in "int" so that others
244 * won't have to do range checks all the time. 244 * won't have to do range checks all the time.
245 */ 245 */
246#define MAX_RW_COUNT (INT_MAX & PAGE_CACHE_MASK)
247
248int rw_verify_area(int read_write, struct file *file, loff_t *ppos, size_t count) 246int rw_verify_area(int read_write, struct file *file, loff_t *ppos, size_t count)
249{ 247{
250 struct inode *inode; 248 struct inode *inode;
@@ -584,65 +582,71 @@ ssize_t rw_copy_check_uvector(int type, const struct iovec __user * uvector,
584 unsigned long nr_segs, unsigned long fast_segs, 582 unsigned long nr_segs, unsigned long fast_segs,
585 struct iovec *fast_pointer, 583 struct iovec *fast_pointer,
586 struct iovec **ret_pointer) 584 struct iovec **ret_pointer)
587 { 585{
588 unsigned long seg; 586 unsigned long seg;
589 ssize_t ret; 587 ssize_t ret;
590 struct iovec *iov = fast_pointer; 588 struct iovec *iov = fast_pointer;
591 589
592 /* 590 /*
593 * SuS says "The readv() function *may* fail if the iovcnt argument 591 * SuS says "The readv() function *may* fail if the iovcnt argument
594 * was less than or equal to 0, or greater than {IOV_MAX}. Linux has 592 * was less than or equal to 0, or greater than {IOV_MAX}. Linux has
595 * traditionally returned zero for zero segments, so... 593 * traditionally returned zero for zero segments, so...
596 */ 594 */
597 if (nr_segs == 0) { 595 if (nr_segs == 0) {
598 ret = 0; 596 ret = 0;
599 goto out; 597 goto out;
600 } 598 }
601 599
602 /* 600 /*
603 * First get the "struct iovec" from user memory and 601 * First get the "struct iovec" from user memory and
604 * verify all the pointers 602 * verify all the pointers
605 */ 603 */
606 if (nr_segs > UIO_MAXIOV) { 604 if (nr_segs > UIO_MAXIOV) {
607 ret = -EINVAL; 605 ret = -EINVAL;
608 goto out; 606 goto out;
609 } 607 }
610 if (nr_segs > fast_segs) { 608 if (nr_segs > fast_segs) {
611 iov = kmalloc(nr_segs*sizeof(struct iovec), GFP_KERNEL); 609 iov = kmalloc(nr_segs*sizeof(struct iovec), GFP_KERNEL);
612 if (iov == NULL) { 610 if (iov == NULL) {
613 ret = -ENOMEM; 611 ret = -ENOMEM;
614 goto out; 612 goto out;
615 } 613 }
616 } 614 }
617 if (copy_from_user(iov, uvector, nr_segs*sizeof(*uvector))) { 615 if (copy_from_user(iov, uvector, nr_segs*sizeof(*uvector))) {
618 ret = -EFAULT; 616 ret = -EFAULT;
619 goto out; 617 goto out;
620 } 618 }
621 619
622 /* 620 /*
623 * According to the Single Unix Specification we should return EINVAL 621 * According to the Single Unix Specification we should return EINVAL
624 * if an element length is < 0 when cast to ssize_t or if the 622 * if an element length is < 0 when cast to ssize_t or if the
625 * total length would overflow the ssize_t return value of the 623 * total length would overflow the ssize_t return value of the
626 * system call. 624 * system call.
627 */ 625 *
626 * Linux caps all read/write calls to MAX_RW_COUNT, and avoids the
627 * overflow case.
628 */
628 ret = 0; 629 ret = 0;
629 for (seg = 0; seg < nr_segs; seg++) { 630 for (seg = 0; seg < nr_segs; seg++) {
630 void __user *buf = iov[seg].iov_base; 631 void __user *buf = iov[seg].iov_base;
631 ssize_t len = (ssize_t)iov[seg].iov_len; 632 ssize_t len = (ssize_t)iov[seg].iov_len;
632 633
633 /* see if we we're about to use an invalid len or if 634 /* see if we we're about to use an invalid len or if
634 * it's about to overflow ssize_t */ 635 * it's about to overflow ssize_t */
635 if (len < 0 || (ret + len < ret)) { 636 if (len < 0) {
636 ret = -EINVAL; 637 ret = -EINVAL;
637 goto out; 638 goto out;
638 } 639 }
639 if (unlikely(!access_ok(vrfy_dir(type), buf, len))) { 640 if (unlikely(!access_ok(vrfy_dir(type), buf, len))) {
640 ret = -EFAULT; 641 ret = -EFAULT;
641 goto out; 642 goto out;
643 }
644 if (len > MAX_RW_COUNT - ret) {
645 len = MAX_RW_COUNT - ret;
646 iov[seg].iov_len = len;
642 } 647 }
643
644 ret += len; 648 ret += len;
645 } 649 }
646out: 650out:
647 *ret_pointer = iov; 651 *ret_pointer = iov;
648 return ret; 652 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
@@ -1867,6 +1867,7 @@ extern int current_umask(void);
1867/* /sys/fs */ 1867/* /sys/fs */
1868extern struct kobject *fs_kobj; 1868extern struct kobject *fs_kobj;
1869 1869
1870#define MAX_RW_COUNT (INT_MAX & PAGE_CACHE_MASK)
1870extern int rw_verify_area(int, struct file *, loff_t *, size_t); 1871extern int rw_verify_area(int, struct file *, loff_t *, size_t);
1871 1872
1872#define FLOCK_VERIFY_READ 1 1873#define FLOCK_VERIFY_READ 1