Several generic user access paths don't go through the copy helpers that check for a NULL current->mm. Add the uaccess_without_mm() check to them as well, so that they warn and fail without attempting the access:
- __scoped_user_access_begin(), covering scoped_user_*_access() and get_user_inline()/put_user_inline() on all architectures, including when masked user access is used. - strnlen_user(). - check_zeroed_user(). - The iov_iter user copy routines that call into architecture-specific helpers: copy_to_user_iter_mc() (copy_mc_to_user()), copy_from_user_iter_nocache() (copy_from_user_inatomic_nontemporal()), copy_from_user_iter_flushcache() (copy_from_user_flushcache()) and zero_to_user_iter() (clear_user()). Only check for a NULL mm here rather than using uaccess_should_fail(), so as not to start injecting failures in these paths. Paths implemented entirely by architectures, e.g. get_user(), put_user(), clear_user(), and direct users of user_access_begin() or masked_user_access_begin(), are still not covered. Signed-off-by: James Houghton <[email protected]> --- include/linux/uaccess.h | 2 ++ lib/iov_iter.c | 8 ++++++++ lib/strnlen_user.c | 3 +++ lib/usercopy.c | 3 +++ 4 files changed, 16 insertions(+) diff --git a/include/linux/uaccess.h b/include/linux/uaccess.h index eae204f7261b..7d26d2de9599 100644 --- a/include/linux/uaccess.h +++ b/include/linux/uaccess.h @@ -781,6 +781,8 @@ static __always_inline void __scoped_user_rw_access_end(const void *p) ({ \ typeof(uptr) __retptr; \ \ + if (uaccess_without_mm()) \ + goto elbl; \ if (can_do_masked_user_access()) { \ __retptr = masked_user_##mode##_access_begin(uptr); \ } else { \ diff --git a/lib/iov_iter.c b/lib/iov_iter.c index 21e2ccf1e8f2..a0769874241c 100644 --- a/lib/iov_iter.c +++ b/lib/iov_iter.c @@ -204,6 +204,8 @@ static __always_inline size_t copy_to_user_iter_mc(void __user *iter_to, size_t progress, size_t len, void *from, void *priv2) { + if (uaccess_without_mm()) + return len; if (access_ok(iter_to, len)) { from += progress; instrument_copy_to_user(iter_to, from, len); @@ -277,6 +279,8 @@ static __always_inline size_t copy_from_user_iter_nocache(void __user *iter_from, size_t progress, size_t len, void *to, void *priv2) { + if (uaccess_without_mm()) + return len; return copy_from_user_inatomic_nontemporal(to + progress, iter_from, len); } @@ -296,6 +300,8 @@ static __always_inline size_t copy_from_user_iter_flushcache(void __user *iter_from, size_t progress, size_t len, void *to, void *priv2) { + if (uaccess_without_mm()) + return len; return copy_from_user_flushcache(to + progress, iter_from, len); } @@ -451,6 +457,8 @@ static __always_inline size_t zero_to_user_iter(void __user *iter_to, size_t progress, size_t len, void *priv, void *priv2) { + if (uaccess_without_mm()) + return len; return clear_user(iter_to, len); } diff --git a/lib/strnlen_user.c b/lib/strnlen_user.c index 4a6574b67f82..45709798eb8f 100644 --- a/lib/strnlen_user.c +++ b/lib/strnlen_user.c @@ -96,6 +96,9 @@ long strnlen_user(const char __user *str, long count) if (unlikely(count <= 0)) return 0; + if (uaccess_without_mm()) + return 0; + if (can_do_masked_user_access()) { long retval; diff --git a/lib/usercopy.c b/lib/usercopy.c index e2f0bf104a59..9146c729c47a 100644 --- a/lib/usercopy.c +++ b/lib/usercopy.c @@ -48,6 +48,9 @@ int check_zeroed_user(const void __user *from, size_t size) if (unlikely(size == 0)) return 1; + if (uaccess_without_mm()) + return -EFAULT; + from -= align; size += align; -- 2.56.0.307.g5fe7bd680c-goog
