aboutsummaryrefslogtreecommitdiffstats
path: root/fs/read_write.c
diff options
context:
space:
mode:
Diffstat (limited to 'fs/read_write.c')
-rw-r--r--fs/read_write.c129
1 files changed, 75 insertions, 54 deletions
diff --git a/fs/read_write.c b/fs/read_write.c
index 4ed839bcb91c..f792000a28e6 100644
--- a/fs/read_write.c
+++ b/fs/read_write.c
@@ -511,6 +511,74 @@ ssize_t do_loop_readv_writev(struct file *filp, struct iovec *iov,
511/* A write operation does a read from user space and vice versa */ 511/* A write operation does a read from user space and vice versa */
512#define vrfy_dir(type) ((type) == READ ? VERIFY_WRITE : VERIFY_READ) 512#define vrfy_dir(type) ((type) == READ ? VERIFY_WRITE : VERIFY_READ)
513 513
514ssize_t rw_copy_check_uvector(int type, const struct iovec __user * uvector,
515 unsigned long nr_segs, unsigned long fast_segs,
516 struct iovec *fast_pointer,
517 struct iovec **ret_pointer)
518 {
519 unsigned long seg;
520 ssize_t ret;
521 struct iovec *iov = fast_pointer;
522
523 /*
524 * SuS says "The readv() function *may* fail if the iovcnt argument
525 * was less than or equal to 0, or greater than {IOV_MAX}. Linux has
526 * traditionally returned zero for zero segments, so...
527 */
528 if (nr_segs == 0) {
529 ret = 0;
530 goto out;
531 }
532
533 /*
534 * First get the "struct iovec" from user memory and
535 * verify all the pointers
536 */
537 if (nr_segs > UIO_MAXIOV) {
538 ret = -EINVAL;
539 goto out;
540 }
541 if (nr_segs > fast_segs) {
542 iov = kmalloc(nr_segs*sizeof(struct iovec), GFP_KERNEL);
543 if (iov == NULL) {
544 ret = -ENOMEM;
545 goto out;
546 }
547 }
548 if (copy_from_user(iov, uvector, nr_segs*sizeof(*uvector))) {
549 ret = -EFAULT;
550 goto out;
551 }
552
553 /*
554 * According to the Single Unix Specification we should return EINVAL
555 * if an element length is < 0 when cast to ssize_t or if the
556 * total length would overflow the ssize_t return value of the
557 * system call.
558 */
559 ret = 0;
560 for (seg = 0; seg < nr_segs; seg++) {
561 void __user *buf = iov[seg].iov_base;
562 ssize_t len = (ssize_t)iov[seg].iov_len;
563
564 /* see if we we're about to use an invalid len or if
565 * it's about to overflow ssize_t */
566 if (len < 0 || (ret + len < ret)) {
567 ret = -EINVAL;
568 goto out;
569 }
570 if (unlikely(!access_ok(vrfy_dir(type), buf, len))) {
571 ret = -EFAULT;
572 goto out;
573 }
574
575 ret += len;
576 }
577out:
578 *ret_pointer = iov;
579 return ret;
580}
581
514static ssize_t do_readv_writev(int type, struct file *file, 582static ssize_t do_readv_writev(int type, struct file *file,
515 const struct iovec __user * uvector, 583 const struct iovec __user * uvector,
516 unsigned long nr_segs, loff_t *pos) 584 unsigned long nr_segs, loff_t *pos)
@@ -519,64 +587,20 @@ static ssize_t do_readv_writev(int type, struct file *file,
519 struct iovec iovstack[UIO_FASTIOV]; 587 struct iovec iovstack[UIO_FASTIOV];
520 struct iovec *iov = iovstack; 588 struct iovec *iov = iovstack;
521 ssize_t ret; 589 ssize_t ret;
522 int seg;
523 io_fn_t fn; 590 io_fn_t fn;
524 iov_fn_t fnv; 591 iov_fn_t fnv;
525 592
526 /* 593 if (!file->f_op) {
527 * SuS says "The readv() function *may* fail if the iovcnt argument 594 ret = -EINVAL;
528 * was less than or equal to 0, or greater than {IOV_MAX}. Linux has
529 * traditionally returned zero for zero segments, so...
530 */
531 ret = 0;
532 if (nr_segs == 0)
533 goto out;
534
535 /*
536 * First get the "struct iovec" from user memory and
537 * verify all the pointers
538 */
539 ret = -EINVAL;
540 if (nr_segs > UIO_MAXIOV)
541 goto out;
542 if (!file->f_op)
543 goto out;
544 if (nr_segs > UIO_FASTIOV) {
545 ret = -ENOMEM;
546 iov = kmalloc(nr_segs*sizeof(struct iovec), GFP_KERNEL);
547 if (!iov)
548 goto out;
549 }
550 ret = -EFAULT;
551 if (copy_from_user(iov, uvector, nr_segs*sizeof(*uvector)))
552 goto out; 595 goto out;
596 }
553 597
554 /* 598 ret = rw_copy_check_uvector(type, uvector, nr_segs,
555 * Single unix specification: 599 ARRAY_SIZE(iovstack), iovstack, &iov);
556 * We should -EINVAL if an element length is not >= 0 and fitting an 600 if (ret <= 0)
557 * ssize_t. The total length is fitting an ssize_t
558 *
559 * Be careful here because iov_len is a size_t not an ssize_t
560 */
561 tot_len = 0;
562 ret = -EINVAL;
563 for (seg = 0; seg < nr_segs; seg++) {
564 void __user *buf = iov[seg].iov_base;
565 ssize_t len = (ssize_t)iov[seg].iov_len;
566
567 if (len < 0) /* size_t not fitting an ssize_t .. */
568 goto out;
569 if (unlikely(!access_ok(vrfy_dir(type), buf, len)))
570 goto Efault;
571 tot_len += len;
572 if ((ssize_t)tot_len < 0) /* maths overflow on the ssize_t */
573 goto out;
574 }
575 if (tot_len == 0) {
576 ret = 0;
577 goto out; 601 goto out;
578 }
579 602
603 tot_len = ret;
580 ret = rw_verify_area(type, file, pos, tot_len); 604 ret = rw_verify_area(type, file, pos, tot_len);
581 if (ret < 0) 605 if (ret < 0)
582 goto out; 606 goto out;
@@ -609,9 +633,6 @@ out:
609 fsnotify_modify(file->f_dentry); 633 fsnotify_modify(file->f_dentry);
610 } 634 }
611 return ret; 635 return ret;
612Efault:
613 ret = -EFAULT;
614 goto out;
615} 636}
616 637
617ssize_t vfs_readv(struct file *file, const struct iovec __user *vec, 638ssize_t vfs_readv(struct file *file, const struct iovec __user *vec,