From: Linus Torvalds Date: Sat, 8 Jul 2017 03:39:20 +0000 (-0700) Subject: Merge branch 'uaccess-work.iov_iter' of git://git.kernel.org/pub/scm/linux/kernel... X-Git-Tag: v4.13-rc1~99 X-Git-Url: https://git.kernelconcepts.de/?p=karo-tx-linux.git;a=commitdiff_plain;h=6a37e94009b1a76d415b2759755f5cc7854c4ff6 Merge branch 'uaccess-work.iov_iter' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs Pull iov_iter hardening from Al Viro: "This is the iov_iter/uaccess/hardening pile. For one thing, it trims the inline part of copy_to_user/copy_from_user to the minimum that *does* need to be inlined - object size checks, basically. For another, it sanitizes the checks for iov_iter primitives. There are 4 groups of checks: access_ok(), might_fault(), object size and KASAN. - access_ok() had been verified by whoever had set the iov_iter up. However, that has happened in a function far away, so proving that there's no path to actual copying bypassing those checks is hard and proving that iov_iter has not been buggered in the meanwhile is also not pleasant. So we want those redone in actual copyin/copyout. - might_fault() is better off consolidated - we know whether it needs to be checked as soon as we enter iov_iter primitive and observe the iov_iter flavour. No need to wait until the copyin/copyout. The call chains are short enough to make sure we won't miss anything - in fact, it's more robust that way, since there are cases where we do e.g. forced fault-in before getting to copyin/copyout. It's not quite what we need to check (in particular, combination of iovec-backed and set_fs(KERNEL_DS) is almost certainly a bug, not a cause to skip checks), but that's for later series. For now let's keep might_fault(). - KASAN checks belong in copyin/copyout - at the same level where other iov_iter flavours would've hit them in memcpy(). - object size checks should apply to *all* iov_iter flavours, not just iovec-backed ones. There are two groups of primitives - one gets the kernel object described as pointer + size (copy_to_iter(), etc.) while another gets it as page + offset + size (copy_page_to_iter(), etc.) For the first group the checks are best done where we actually have a chance to find the object size. In other words, those belong in inline wrappers in uio.h, before calling into iov_iter.c. Same kind as we have for inlined part of copy_to_user(). For the second group there is no object to look at - offset in page is just a number, it bears no type information. So we do them in the common helper called by iov_iter.c primitives of that kind. All it currently does is checking that we are not trying to access outside of the compound page; eventually we might want to add some sanity checks on the page involved. So the things we need in copyin/copyout part of iov_iter.c do not quite match anything in uaccess.h (we want no zeroing, we *do* want access_ok() and KASAN and we want no might_fault() or object size checks done on that level). OTOH, these needs are simple enough to provide a couple of helpers (static in iov_iter.c) doing just what we need..." * 'uaccess-work.iov_iter' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs: iov_iter: saner checks on copyin/copyout iov_iter: sanity checks for copy to/from page primitives iov_iter/hardening: move object size checks to inlined part copy_{to,from}_user(): consolidate object size checks copy_{from,to}_user(): move kasan checks and might_fault() out-of-line --- 6a37e94009b1a76d415b2759755f5cc7854c4ff6 diff --cc include/linux/uio.h index 55cd54a0e941,243e2362fe1a..342d2dc225b9 --- a/include/linux/uio.h +++ b/include/linux/uio.h @@@ -91,26 -92,58 +92,79 @@@ size_t copy_page_to_iter(struct page *p struct iov_iter *i); size_t copy_page_from_iter(struct page *page, size_t offset, size_t bytes, struct iov_iter *i); - size_t copy_to_iter(const void *addr, size_t bytes, struct iov_iter *i); - size_t copy_from_iter(void *addr, size_t bytes, struct iov_iter *i); - bool copy_from_iter_full(void *addr, size_t bytes, struct iov_iter *i); - size_t copy_from_iter_nocache(void *addr, size_t bytes, struct iov_iter *i); + + size_t _copy_to_iter(const void *addr, size_t bytes, struct iov_iter *i); + size_t _copy_from_iter(void *addr, size_t bytes, struct iov_iter *i); + bool _copy_from_iter_full(void *addr, size_t bytes, struct iov_iter *i); + size_t _copy_from_iter_nocache(void *addr, size_t bytes, struct iov_iter *i); + bool _copy_from_iter_full_nocache(void *addr, size_t bytes, struct iov_iter *i); + + static __always_inline __must_check + size_t copy_to_iter(const void *addr, size_t bytes, struct iov_iter *i) + { + if (unlikely(!check_copy_size(addr, bytes, true))) + return bytes; + else + return _copy_to_iter(addr, bytes, i); + } + + static __always_inline __must_check + size_t copy_from_iter(void *addr, size_t bytes, struct iov_iter *i) + { + if (unlikely(!check_copy_size(addr, bytes, false))) + return bytes; + else + return _copy_from_iter(addr, bytes, i); + } + + static __always_inline __must_check + bool copy_from_iter_full(void *addr, size_t bytes, struct iov_iter *i) + { + if (unlikely(!check_copy_size(addr, bytes, false))) + return false; + else + return _copy_from_iter_full(addr, bytes, i); + } + + static __always_inline __must_check + size_t copy_from_iter_nocache(void *addr, size_t bytes, struct iov_iter *i) + { + if (unlikely(!check_copy_size(addr, bytes, false))) + return bytes; + else + return _copy_from_iter_nocache(addr, bytes, i); + } + + static __always_inline __must_check + bool copy_from_iter_full_nocache(void *addr, size_t bytes, struct iov_iter *i) + { + if (unlikely(!check_copy_size(addr, bytes, false))) + return false; + else + return _copy_from_iter_full_nocache(addr, bytes, i); + } + +#ifdef CONFIG_ARCH_HAS_UACCESS_FLUSHCACHE +/* + * Note, users like pmem that depend on the stricter semantics of + * copy_from_iter_flushcache() than copy_from_iter_nocache() must check for + * IS_ENABLED(CONFIG_ARCH_HAS_UACCESS_FLUSHCACHE) before assuming that the + * destination is flushed from the cache on return. + */ - size_t copy_from_iter_flushcache(void *addr, size_t bytes, struct iov_iter *i); ++size_t _copy_from_iter_flushcache(void *addr, size_t bytes, struct iov_iter *i); +#else - static inline size_t copy_from_iter_flushcache(void *addr, size_t bytes, - struct iov_iter *i) ++#define _copy_from_iter_flushcache _copy_from_iter_nocache ++#endif ++ ++static __always_inline __must_check ++size_t copy_from_iter_flushcache(void *addr, size_t bytes, struct iov_iter *i) +{ - return copy_from_iter_nocache(addr, bytes, i); ++ if (unlikely(!check_copy_size(addr, bytes, false))) ++ return bytes; ++ else ++ return _copy_from_iter_flushcache(addr, bytes, i); +} - #endif - bool copy_from_iter_full_nocache(void *addr, size_t bytes, struct iov_iter *i); ++ size_t iov_iter_zero(size_t bytes, struct iov_iter *); unsigned long iov_iter_alignment(const struct iov_iter *i); unsigned long iov_iter_gap_alignment(const struct iov_iter *i); diff --cc lib/iov_iter.c index c9a69064462f,b3b2ee8a20b5..52c8dd6d8e82 --- a/lib/iov_iter.c +++ b/lib/iov_iter.c @@@ -613,31 -637,9 +637,31 @@@ size_t _copy_from_iter_nocache(void *ad return bytes; } - EXPORT_SYMBOL(copy_from_iter_nocache); + EXPORT_SYMBOL(_copy_from_iter_nocache); +#ifdef CONFIG_ARCH_HAS_UACCESS_FLUSHCACHE - size_t copy_from_iter_flushcache(void *addr, size_t bytes, struct iov_iter *i) ++size_t _copy_from_iter_flushcache(void *addr, size_t bytes, struct iov_iter *i) +{ + char *to = addr; + if (unlikely(i->type & ITER_PIPE)) { + WARN_ON(1); + return 0; + } + iterate_and_advance(i, bytes, v, + __copy_from_user_flushcache((to += v.iov_len) - v.iov_len, + v.iov_base, v.iov_len), + memcpy_page_flushcache((to += v.bv_len) - v.bv_len, v.bv_page, + v.bv_offset, v.bv_len), + memcpy_flushcache((to += v.iov_len) - v.iov_len, v.iov_base, + v.iov_len) + ) + + return bytes; +} - EXPORT_SYMBOL_GPL(copy_from_iter_flushcache); ++EXPORT_SYMBOL_GPL(_copy_from_iter_flushcache); +#endif + - bool copy_from_iter_full_nocache(void *addr, size_t bytes, struct iov_iter *i) + bool _copy_from_iter_full_nocache(void *addr, size_t bytes, struct iov_iter *i) { char *to = addr; if (unlikely(i->type & ITER_PIPE)) {