diff options
author | Al Viro <viro@zeniv.linux.org.uk> | 2015-03-21 17:45:43 -0400 |
---|---|---|
committer | Al Viro <viro@zeniv.linux.org.uk> | 2015-03-30 11:08:16 -0400 |
commit | bc917be8105993c256338ad1189650364a741483 (patch) | |
tree | f97b0c3e30bffabf49ac03c63ee362859ac76852 /lib | |
parent | d879cb83417a71c435f1263e1160a9fce8e95d87 (diff) |
saner iov_iter initialization primitives
iovec-backed iov_iter instances are assumed to satisfy several properties:
* no more than UIO_MAXIOV elements in iovec array
* total size of all ranges is no more than MAX_RW_COUNT
* all ranges pass access_ok().
The problem is, invariants of data structures should be established in the
primitives creating those data structures, not in the code using those
primitives. And iov_iter_init() violates that principle. For a while we
managed to get away with that, but once the use of iov_iter started to
spread, it didn't take long for shit to hit the fan - missed check in
sys_sendto() had introduced a roothole.
We _do_ have primitives for importing and validating iovecs (both native and
compat ones) and those primitives are almost always followed by shoving the
resulting iovec into iov_iter. Life would be considerably simpler (and safer)
if we combined those primitives with initializing iov_iter.
That gives us two new primitives - import_iovec() and compat_import_iovec().
Calling conventions:
iovec = iov_array;
err = import_iovec(direction, uvec, nr_segs,
ARRAY_SIZE(iov_array), &iovec,
&iter);
imports user vector into kernel space (into iov_array if it fits, allocated
if it doesn't fit or if iovec was NULL), validates it and sets iter up to
refer to it. On success 0 is returned and allocated kernel copy (or NULL
if the array had fit into caller-supplied one) is returned via iovec.
On failure all allocations are undone and -E... is returned. If the total
size of ranges exceeds MAX_RW_COUNT, the excess is silently truncated.
compat_import_iovec() expects uvec to be a pointer to user array of compat_iovec;
otherwise it's identical to import_iovec().
Finally, import_single_range() sets iov_iter backed by single-element iovec
covering a user-supplied range -
err = import_single_range(direction, address, size, iovec, &iter);
does validation and sets iter up. Again, size in excess of MAX_RW_COUNT gets
silently truncated.
Next commits will be switching the things up to use of those and reducing
the amount of iov_iter_init() instances.
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
Diffstat (limited to 'lib')
-rw-r--r-- | lib/iov_iter.c | 57 |
1 files changed, 57 insertions, 0 deletions
diff --git a/lib/iov_iter.c b/lib/iov_iter.c index 9d96e283520c..fc6e33f6b7f3 100644 --- a/lib/iov_iter.c +++ b/lib/iov_iter.c | |||
@@ -766,3 +766,60 @@ const void *dup_iter(struct iov_iter *new, struct iov_iter *old, gfp_t flags) | |||
766 | flags); | 766 | flags); |
767 | } | 767 | } |
768 | EXPORT_SYMBOL(dup_iter); | 768 | EXPORT_SYMBOL(dup_iter); |
769 | |||
770 | int import_iovec(int type, const struct iovec __user * uvector, | ||
771 | unsigned nr_segs, unsigned fast_segs, | ||
772 | struct iovec **iov, struct iov_iter *i) | ||
773 | { | ||
774 | ssize_t n; | ||
775 | struct iovec *p; | ||
776 | n = rw_copy_check_uvector(type, uvector, nr_segs, fast_segs, | ||
777 | *iov, &p); | ||
778 | if (n < 0) { | ||
779 | if (p != *iov) | ||
780 | kfree(p); | ||
781 | *iov = NULL; | ||
782 | return n; | ||
783 | } | ||
784 | iov_iter_init(i, type, p, nr_segs, n); | ||
785 | *iov = p == *iov ? NULL : p; | ||
786 | return 0; | ||
787 | } | ||
788 | EXPORT_SYMBOL(import_iovec); | ||
789 | |||
790 | #ifdef CONFIG_COMPAT | ||
791 | #include <linux/compat.h> | ||
792 | |||
793 | int compat_import_iovec(int type, const struct compat_iovec __user * uvector, | ||
794 | unsigned nr_segs, unsigned fast_segs, | ||
795 | struct iovec **iov, struct iov_iter *i) | ||
796 | { | ||
797 | ssize_t n; | ||
798 | struct iovec *p; | ||
799 | n = compat_rw_copy_check_uvector(type, uvector, nr_segs, fast_segs, | ||
800 | *iov, &p); | ||
801 | if (n < 0) { | ||
802 | if (p != *iov) | ||
803 | kfree(p); | ||
804 | *iov = NULL; | ||
805 | return n; | ||
806 | } | ||
807 | iov_iter_init(i, type, p, nr_segs, n); | ||
808 | *iov = p == *iov ? NULL : p; | ||
809 | return 0; | ||
810 | } | ||
811 | #endif | ||
812 | |||
813 | int import_single_range(int rw, void __user *buf, size_t len, | ||
814 | struct iovec *iov, struct iov_iter *i) | ||
815 | { | ||
816 | if (len > MAX_RW_COUNT) | ||
817 | len = MAX_RW_COUNT; | ||
818 | if (unlikely(!access_ok(!rw, buf, len))) | ||
819 | return -EFAULT; | ||
820 | |||
821 | iov->iov_base = buf; | ||
822 | iov->iov_len = len; | ||
823 | iov_iter_init(i, rw, iov, 1, len); | ||
824 | return 0; | ||
825 | } | ||