diff options
Diffstat (limited to 'fs/read_write.c')
-rw-r--r-- | fs/read_write.c | 129 |
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 | ||
514 | ssize_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 | } | ||
577 | out: | ||
578 | *ret_pointer = iov; | ||
579 | return ret; | ||
580 | } | ||
581 | |||
514 | static ssize_t do_readv_writev(int type, struct file *file, | 582 | static 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; |
612 | Efault: | ||
613 | ret = -EFAULT; | ||
614 | goto out; | ||
615 | } | 636 | } |
616 | 637 | ||
617 | ssize_t vfs_readv(struct file *file, const struct iovec __user *vec, | 638 | ssize_t vfs_readv(struct file *file, const struct iovec __user *vec, |