diff options
Diffstat (limited to 'fs/compat.c')
| -rw-r--r-- | fs/compat.c | 132 |
1 files changed, 78 insertions, 54 deletions
diff --git a/fs/compat.c b/fs/compat.c index 05448730f840..f0b391c50552 100644 --- a/fs/compat.c +++ b/fs/compat.c | |||
| @@ -568,6 +568,79 @@ out: | |||
| 568 | return ret; | 568 | return ret; |
| 569 | } | 569 | } |
| 570 | 570 | ||
| 571 | /* A write operation does a read from user space and vice versa */ | ||
| 572 | #define vrfy_dir(type) ((type) == READ ? VERIFY_WRITE : VERIFY_READ) | ||
| 573 | |||
| 574 | ssize_t compat_rw_copy_check_uvector(int type, | ||
| 575 | const struct compat_iovec __user *uvector, unsigned long nr_segs, | ||
| 576 | unsigned long fast_segs, struct iovec *fast_pointer, | ||
| 577 | struct iovec **ret_pointer) | ||
| 578 | { | ||
| 579 | compat_ssize_t tot_len; | ||
| 580 | struct iovec *iov = *ret_pointer = fast_pointer; | ||
| 581 | ssize_t ret = 0; | ||
| 582 | int seg; | ||
| 583 | |||
| 584 | /* | ||
| 585 | * SuS says "The readv() function *may* fail if the iovcnt argument | ||
| 586 | * was less than or equal to 0, or greater than {IOV_MAX}. Linux has | ||
| 587 | * traditionally returned zero for zero segments, so... | ||
| 588 | */ | ||
| 589 | if (nr_segs == 0) | ||
| 590 | goto out; | ||
| 591 | |||
| 592 | ret = -EINVAL; | ||
| 593 | if (nr_segs > UIO_MAXIOV || nr_segs < 0) | ||
| 594 | goto out; | ||
| 595 | if (nr_segs > fast_segs) { | ||
| 596 | ret = -ENOMEM; | ||
| 597 | iov = kmalloc(nr_segs*sizeof(struct iovec), GFP_KERNEL); | ||
| 598 | if (iov == NULL) { | ||
| 599 | *ret_pointer = fast_pointer; | ||
| 600 | goto out; | ||
| 601 | } | ||
| 602 | } | ||
| 603 | *ret_pointer = iov; | ||
| 604 | |||
| 605 | /* | ||
| 606 | * Single unix specification: | ||
| 607 | * We should -EINVAL if an element length is not >= 0 and fitting an | ||
| 608 | * ssize_t. The total length is fitting an ssize_t | ||
| 609 | * | ||
| 610 | * Be careful here because iov_len is a size_t not an ssize_t | ||
| 611 | */ | ||
| 612 | tot_len = 0; | ||
| 613 | ret = -EINVAL; | ||
| 614 | for (seg = 0; seg < nr_segs; seg++) { | ||
| 615 | compat_ssize_t tmp = tot_len; | ||
| 616 | compat_uptr_t buf; | ||
| 617 | compat_ssize_t len; | ||
| 618 | |||
| 619 | if (__get_user(len, &uvector->iov_len) || | ||
| 620 | __get_user(buf, &uvector->iov_base)) { | ||
| 621 | ret = -EFAULT; | ||
| 622 | goto out; | ||
| 623 | } | ||
| 624 | if (len < 0) /* size_t not fitting in compat_ssize_t .. */ | ||
| 625 | goto out; | ||
| 626 | tot_len += len; | ||
| 627 | if (tot_len < tmp) /* maths overflow on the compat_ssize_t */ | ||
| 628 | goto out; | ||
| 629 | if (!access_ok(vrfy_dir(type), buf, len)) { | ||
| 630 | ret = -EFAULT; | ||
| 631 | goto out; | ||
| 632 | } | ||
| 633 | iov->iov_base = compat_ptr(buf); | ||
| 634 | iov->iov_len = (compat_size_t) len; | ||
| 635 | uvector++; | ||
| 636 | iov++; | ||
| 637 | } | ||
| 638 | ret = tot_len; | ||
| 639 | |||
| 640 | out: | ||
| 641 | return ret; | ||
| 642 | } | ||
| 643 | |||
| 571 | static inline long | 644 | static inline long |
| 572 | copy_iocb(long nr, u32 __user *ptr32, struct iocb __user * __user *ptr64) | 645 | copy_iocb(long nr, u32 __user *ptr32, struct iocb __user * __user *ptr64) |
| 573 | { | 646 | { |
| @@ -600,7 +673,7 @@ compat_sys_io_submit(aio_context_t ctx_id, int nr, u32 __user *iocb) | |||
| 600 | iocb64 = compat_alloc_user_space(nr * sizeof(*iocb64)); | 673 | iocb64 = compat_alloc_user_space(nr * sizeof(*iocb64)); |
| 601 | ret = copy_iocb(nr, iocb, iocb64); | 674 | ret = copy_iocb(nr, iocb, iocb64); |
| 602 | if (!ret) | 675 | if (!ret) |
| 603 | ret = sys_io_submit(ctx_id, nr, iocb64); | 676 | ret = do_io_submit(ctx_id, nr, iocb64, 1); |
| 604 | return ret; | 677 | return ret; |
| 605 | } | 678 | } |
| 606 | 679 | ||
| @@ -1077,70 +1150,21 @@ static ssize_t compat_do_readv_writev(int type, struct file *file, | |||
| 1077 | { | 1150 | { |
| 1078 | compat_ssize_t tot_len; | 1151 | compat_ssize_t tot_len; |
| 1079 | struct iovec iovstack[UIO_FASTIOV]; | 1152 | struct iovec iovstack[UIO_FASTIOV]; |
| 1080 | struct iovec *iov=iovstack, *vector; | 1153 | struct iovec *iov; |
| 1081 | ssize_t ret; | 1154 | ssize_t ret; |
| 1082 | int seg; | ||
| 1083 | io_fn_t fn; | 1155 | io_fn_t fn; |
| 1084 | iov_fn_t fnv; | 1156 | iov_fn_t fnv; |
| 1085 | 1157 | ||
| 1086 | /* | ||
| 1087 | * SuS says "The readv() function *may* fail if the iovcnt argument | ||
| 1088 | * was less than or equal to 0, or greater than {IOV_MAX}. Linux has | ||
| 1089 | * traditionally returned zero for zero segments, so... | ||
| 1090 | */ | ||
| 1091 | ret = 0; | ||
| 1092 | if (nr_segs == 0) | ||
| 1093 | goto out; | ||
| 1094 | |||
| 1095 | /* | ||
| 1096 | * First get the "struct iovec" from user memory and | ||
| 1097 | * verify all the pointers | ||
| 1098 | */ | ||
| 1099 | ret = -EINVAL; | 1158 | ret = -EINVAL; |
| 1100 | if ((nr_segs > UIO_MAXIOV) || (nr_segs <= 0)) | ||
| 1101 | goto out; | ||
| 1102 | if (!file->f_op) | 1159 | if (!file->f_op) |
| 1103 | goto out; | 1160 | goto out; |
| 1104 | if (nr_segs > UIO_FASTIOV) { | 1161 | |
| 1105 | ret = -ENOMEM; | ||
| 1106 | iov = kmalloc(nr_segs*sizeof(struct iovec), GFP_KERNEL); | ||
| 1107 | if (!iov) | ||
| 1108 | goto out; | ||
| 1109 | } | ||
| 1110 | ret = -EFAULT; | 1162 | ret = -EFAULT; |
| 1111 | if (!access_ok(VERIFY_READ, uvector, nr_segs*sizeof(*uvector))) | 1163 | if (!access_ok(VERIFY_READ, uvector, nr_segs*sizeof(*uvector))) |
| 1112 | goto out; | 1164 | goto out; |
| 1113 | 1165 | ||
| 1114 | /* | 1166 | tot_len = compat_rw_copy_check_uvector(type, uvector, nr_segs, |
| 1115 | * Single unix specification: | 1167 | UIO_FASTIOV, iovstack, &iov); |
| 1116 | * We should -EINVAL if an element length is not >= 0 and fitting an | ||
| 1117 | * ssize_t. The total length is fitting an ssize_t | ||
| 1118 | * | ||
| 1119 | * Be careful here because iov_len is a size_t not an ssize_t | ||
| 1120 | */ | ||
| 1121 | tot_len = 0; | ||
| 1122 | vector = iov; | ||
| 1123 | ret = -EINVAL; | ||
| 1124 | for (seg = 0 ; seg < nr_segs; seg++) { | ||
| 1125 | compat_ssize_t tmp = tot_len; | ||
| 1126 | compat_ssize_t len; | ||
| 1127 | compat_uptr_t buf; | ||
| 1128 | |||
| 1129 | if (__get_user(len, &uvector->iov_len) || | ||
| 1130 | __get_user(buf, &uvector->iov_base)) { | ||
| 1131 | ret = -EFAULT; | ||
| 1132 | goto out; | ||
| 1133 | } | ||
| 1134 | if (len < 0) /* size_t not fitting an compat_ssize_t .. */ | ||
| 1135 | goto out; | ||
| 1136 | tot_len += len; | ||
| 1137 | if (tot_len < tmp) /* maths overflow on the compat_ssize_t */ | ||
| 1138 | goto out; | ||
| 1139 | vector->iov_base = compat_ptr(buf); | ||
| 1140 | vector->iov_len = (compat_size_t) len; | ||
| 1141 | uvector++; | ||
| 1142 | vector++; | ||
| 1143 | } | ||
| 1144 | if (tot_len == 0) { | 1168 | if (tot_len == 0) { |
| 1145 | ret = 0; | 1169 | ret = 0; |
| 1146 | goto out; | 1170 | goto out; |
