Commit 6a37e940 authored by Linus Torvalds's avatar Linus Torvalds

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
parents da029c11 09fc68dc
...@@ -113,6 +113,33 @@ static inline void check_object_size(const void *ptr, unsigned long n, ...@@ -113,6 +113,33 @@ static inline void check_object_size(const void *ptr, unsigned long n,
{ } { }
#endif /* CONFIG_HARDENED_USERCOPY */ #endif /* CONFIG_HARDENED_USERCOPY */
extern void __compiletime_error("copy source size is too small")
__bad_copy_from(void);
extern void __compiletime_error("copy destination size is too small")
__bad_copy_to(void);
static inline void copy_overflow(int size, unsigned long count)
{
WARN(1, "Buffer overflow detected (%d < %lu)!\n", size, count);
}
static __always_inline bool
check_copy_size(const void *addr, size_t bytes, bool is_source)
{
int sz = __compiletime_object_size(addr);
if (unlikely(sz >= 0 && sz < bytes)) {
if (!__builtin_constant_p(bytes))
copy_overflow(sz, bytes);
else if (is_source)
__bad_copy_from();
else
__bad_copy_to();
return false;
}
check_object_size(addr, bytes, is_source);
return true;
}
#ifndef arch_setup_new_exec #ifndef arch_setup_new_exec
static inline void arch_setup_new_exec(void) { } static inline void arch_setup_new_exec(void) { }
#endif #endif
......
...@@ -109,8 +109,11 @@ static inline unsigned long ...@@ -109,8 +109,11 @@ static inline unsigned long
_copy_from_user(void *to, const void __user *from, unsigned long n) _copy_from_user(void *to, const void __user *from, unsigned long n)
{ {
unsigned long res = n; unsigned long res = n;
if (likely(access_ok(VERIFY_READ, from, n))) might_fault();
if (likely(access_ok(VERIFY_READ, from, n))) {
kasan_check_write(to, n);
res = raw_copy_from_user(to, from, n); res = raw_copy_from_user(to, from, n);
}
if (unlikely(res)) if (unlikely(res))
memset(to + (n - res), 0, res); memset(to + (n - res), 0, res);
return res; return res;
...@@ -124,8 +127,11 @@ _copy_from_user(void *, const void __user *, unsigned long); ...@@ -124,8 +127,11 @@ _copy_from_user(void *, const void __user *, unsigned long);
static inline unsigned long static inline unsigned long
_copy_to_user(void __user *to, const void *from, unsigned long n) _copy_to_user(void __user *to, const void *from, unsigned long n)
{ {
if (access_ok(VERIFY_WRITE, to, n)) might_fault();
if (access_ok(VERIFY_WRITE, to, n)) {
kasan_check_read(from, n);
n = raw_copy_to_user(to, from, n); n = raw_copy_to_user(to, from, n);
}
return n; return n;
} }
#else #else
...@@ -133,49 +139,19 @@ extern unsigned long ...@@ -133,49 +139,19 @@ extern unsigned long
_copy_to_user(void __user *, const void *, unsigned long); _copy_to_user(void __user *, const void *, unsigned long);
#endif #endif
extern void __compiletime_error("usercopy buffer size is too small")
__bad_copy_user(void);
static inline void copy_user_overflow(int size, unsigned long count)
{
WARN(1, "Buffer overflow detected (%d < %lu)!\n", size, count);
}
static __always_inline unsigned long __must_check static __always_inline unsigned long __must_check
copy_from_user(void *to, const void __user *from, unsigned long n) copy_from_user(void *to, const void __user *from, unsigned long n)
{ {
int sz = __compiletime_object_size(to); if (likely(check_copy_size(to, n, false)))
might_fault();
kasan_check_write(to, n);
if (likely(sz < 0 || sz >= n)) {
check_object_size(to, n, false);
n = _copy_from_user(to, from, n); n = _copy_from_user(to, from, n);
} else if (!__builtin_constant_p(n))
copy_user_overflow(sz, n);
else
__bad_copy_user();
return n; return n;
} }
static __always_inline unsigned long __must_check static __always_inline unsigned long __must_check
copy_to_user(void __user *to, const void *from, unsigned long n) copy_to_user(void __user *to, const void *from, unsigned long n)
{ {
int sz = __compiletime_object_size(from); if (likely(check_copy_size(from, n, true)))
kasan_check_read(from, n);
might_fault();
if (likely(sz < 0 || sz >= n)) {
check_object_size(from, n, true);
n = _copy_to_user(to, from, n); n = _copy_to_user(to, from, n);
} else if (!__builtin_constant_p(n))
copy_user_overflow(sz, n);
else
__bad_copy_user();
return n; return n;
} }
#ifdef CONFIG_COMPAT #ifdef CONFIG_COMPAT
......
...@@ -10,6 +10,7 @@ ...@@ -10,6 +10,7 @@
#define __LINUX_UIO_H #define __LINUX_UIO_H
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/thread_info.h>
#include <uapi/linux/uio.h> #include <uapi/linux/uio.h>
struct page; struct page;
...@@ -91,10 +92,58 @@ size_t copy_page_to_iter(struct page *page, size_t offset, size_t bytes, ...@@ -91,10 +92,58 @@ size_t copy_page_to_iter(struct page *page, size_t offset, size_t bytes,
struct iov_iter *i); struct iov_iter *i);
size_t copy_page_from_iter(struct page *page, size_t offset, size_t bytes, size_t copy_page_from_iter(struct page *page, size_t offset, size_t bytes,
struct iov_iter *i); 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); size_t _copy_to_iter(const 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(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(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 #ifdef CONFIG_ARCH_HAS_UACCESS_FLUSHCACHE
/* /*
* Note, users like pmem that depend on the stricter semantics of * Note, users like pmem that depend on the stricter semantics of
...@@ -102,15 +151,20 @@ size_t copy_from_iter_nocache(void *addr, size_t bytes, struct iov_iter *i); ...@@ -102,15 +151,20 @@ size_t copy_from_iter_nocache(void *addr, size_t bytes, struct iov_iter *i);
* IS_ENABLED(CONFIG_ARCH_HAS_UACCESS_FLUSHCACHE) before assuming that the * IS_ENABLED(CONFIG_ARCH_HAS_UACCESS_FLUSHCACHE) before assuming that the
* destination is flushed from the cache on return. * 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 #else
static inline size_t copy_from_iter_flushcache(void *addr, size_t bytes, #define _copy_from_iter_flushcache _copy_from_iter_nocache
struct iov_iter *i) #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 *); 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_alignment(const struct iov_iter *i);
unsigned long iov_iter_gap_alignment(const struct iov_iter *i); unsigned long iov_iter_gap_alignment(const struct iov_iter *i);
......
...@@ -130,6 +130,24 @@ ...@@ -130,6 +130,24 @@
} \ } \
} }
static int copyout(void __user *to, const void *from, size_t n)
{
if (access_ok(VERIFY_WRITE, to, n)) {
kasan_check_read(from, n);
n = raw_copy_to_user(to, from, n);
}
return n;
}
static int copyin(void *to, const void __user *from, size_t n)
{
if (access_ok(VERIFY_READ, from, n)) {
kasan_check_write(to, n);
n = raw_copy_from_user(to, from, n);
}
return n;
}
static size_t copy_page_to_iter_iovec(struct page *page, size_t offset, size_t bytes, static size_t copy_page_to_iter_iovec(struct page *page, size_t offset, size_t bytes,
struct iov_iter *i) struct iov_iter *i)
{ {
...@@ -144,6 +162,7 @@ static size_t copy_page_to_iter_iovec(struct page *page, size_t offset, size_t b ...@@ -144,6 +162,7 @@ static size_t copy_page_to_iter_iovec(struct page *page, size_t offset, size_t b
if (unlikely(!bytes)) if (unlikely(!bytes))
return 0; return 0;
might_fault();
wanted = bytes; wanted = bytes;
iov = i->iov; iov = i->iov;
skip = i->iov_offset; skip = i->iov_offset;
...@@ -155,7 +174,7 @@ static size_t copy_page_to_iter_iovec(struct page *page, size_t offset, size_t b ...@@ -155,7 +174,7 @@ static size_t copy_page_to_iter_iovec(struct page *page, size_t offset, size_t b
from = kaddr + offset; from = kaddr + offset;
/* first chunk, usually the only one */ /* first chunk, usually the only one */
left = __copy_to_user_inatomic(buf, from, copy); left = copyout(buf, from, copy);
copy -= left; copy -= left;
skip += copy; skip += copy;
from += copy; from += copy;
...@@ -165,7 +184,7 @@ static size_t copy_page_to_iter_iovec(struct page *page, size_t offset, size_t b ...@@ -165,7 +184,7 @@ static size_t copy_page_to_iter_iovec(struct page *page, size_t offset, size_t b
iov++; iov++;
buf = iov->iov_base; buf = iov->iov_base;
copy = min(bytes, iov->iov_len); copy = min(bytes, iov->iov_len);
left = __copy_to_user_inatomic(buf, from, copy); left = copyout(buf, from, copy);
copy -= left; copy -= left;
skip = copy; skip = copy;
from += copy; from += copy;
...@@ -184,7 +203,7 @@ static size_t copy_page_to_iter_iovec(struct page *page, size_t offset, size_t b ...@@ -184,7 +203,7 @@ static size_t copy_page_to_iter_iovec(struct page *page, size_t offset, size_t b
kaddr = kmap(page); kaddr = kmap(page);
from = kaddr + offset; from = kaddr + offset;
left = __copy_to_user(buf, from, copy); left = copyout(buf, from, copy);
copy -= left; copy -= left;
skip += copy; skip += copy;
from += copy; from += copy;
...@@ -193,7 +212,7 @@ static size_t copy_page_to_iter_iovec(struct page *page, size_t offset, size_t b ...@@ -193,7 +212,7 @@ static size_t copy_page_to_iter_iovec(struct page *page, size_t offset, size_t b
iov++; iov++;
buf = iov->iov_base; buf = iov->iov_base;
copy = min(bytes, iov->iov_len); copy = min(bytes, iov->iov_len);
left = __copy_to_user(buf, from, copy); left = copyout(buf, from, copy);
copy -= left; copy -= left;
skip = copy; skip = copy;
from += copy; from += copy;
...@@ -227,6 +246,7 @@ static size_t copy_page_from_iter_iovec(struct page *page, size_t offset, size_t ...@@ -227,6 +246,7 @@ static size_t copy_page_from_iter_iovec(struct page *page, size_t offset, size_t
if (unlikely(!bytes)) if (unlikely(!bytes))
return 0; return 0;
might_fault();
wanted = bytes; wanted = bytes;
iov = i->iov; iov = i->iov;
skip = i->iov_offset; skip = i->iov_offset;
...@@ -238,7 +258,7 @@ static size_t copy_page_from_iter_iovec(struct page *page, size_t offset, size_t ...@@ -238,7 +258,7 @@ static size_t copy_page_from_iter_iovec(struct page *page, size_t offset, size_t
to = kaddr + offset; to = kaddr + offset;
/* first chunk, usually the only one */ /* first chunk, usually the only one */
left = __copy_from_user_inatomic(to, buf, copy); left = copyin(to, buf, copy);
copy -= left; copy -= left;
skip += copy; skip += copy;
to += copy; to += copy;
...@@ -248,7 +268,7 @@ static size_t copy_page_from_iter_iovec(struct page *page, size_t offset, size_t ...@@ -248,7 +268,7 @@ static size_t copy_page_from_iter_iovec(struct page *page, size_t offset, size_t
iov++; iov++;
buf = iov->iov_base; buf = iov->iov_base;
copy = min(bytes, iov->iov_len); copy = min(bytes, iov->iov_len);
left = __copy_from_user_inatomic(to, buf, copy); left = copyin(to, buf, copy);
copy -= left; copy -= left;
skip = copy; skip = copy;
to += copy; to += copy;
...@@ -267,7 +287,7 @@ static size_t copy_page_from_iter_iovec(struct page *page, size_t offset, size_t ...@@ -267,7 +287,7 @@ static size_t copy_page_from_iter_iovec(struct page *page, size_t offset, size_t
kaddr = kmap(page); kaddr = kmap(page);
to = kaddr + offset; to = kaddr + offset;
left = __copy_from_user(to, buf, copy); left = copyin(to, buf, copy);
copy -= left; copy -= left;
skip += copy; skip += copy;
to += copy; to += copy;
...@@ -276,7 +296,7 @@ static size_t copy_page_from_iter_iovec(struct page *page, size_t offset, size_t ...@@ -276,7 +296,7 @@ static size_t copy_page_from_iter_iovec(struct page *page, size_t offset, size_t
iov++; iov++;
buf = iov->iov_base; buf = iov->iov_base;
copy = min(bytes, iov->iov_len); copy = min(bytes, iov->iov_len);
left = __copy_from_user(to, buf, copy); left = copyin(to, buf, copy);
copy -= left; copy -= left;
skip = copy; skip = copy;
to += copy; to += copy;
...@@ -535,14 +555,15 @@ static size_t copy_pipe_to_iter(const void *addr, size_t bytes, ...@@ -535,14 +555,15 @@ static size_t copy_pipe_to_iter(const void *addr, size_t bytes,
return bytes; return bytes;
} }
size_t copy_to_iter(const void *addr, size_t bytes, struct iov_iter *i) size_t _copy_to_iter(const void *addr, size_t bytes, struct iov_iter *i)
{ {
const char *from = addr; const char *from = addr;
if (unlikely(i->type & ITER_PIPE)) if (unlikely(i->type & ITER_PIPE))
return copy_pipe_to_iter(addr, bytes, i); return copy_pipe_to_iter(addr, bytes, i);
if (iter_is_iovec(i))
might_fault();
iterate_and_advance(i, bytes, v, iterate_and_advance(i, bytes, v,
__copy_to_user(v.iov_base, (from += v.iov_len) - v.iov_len, copyout(v.iov_base, (from += v.iov_len) - v.iov_len, v.iov_len),
v.iov_len),
memcpy_to_page(v.bv_page, v.bv_offset, memcpy_to_page(v.bv_page, v.bv_offset,
(from += v.bv_len) - v.bv_len, v.bv_len), (from += v.bv_len) - v.bv_len, v.bv_len),
memcpy(v.iov_base, (from += v.iov_len) - v.iov_len, v.iov_len) memcpy(v.iov_base, (from += v.iov_len) - v.iov_len, v.iov_len)
...@@ -550,18 +571,19 @@ size_t copy_to_iter(const void *addr, size_t bytes, struct iov_iter *i) ...@@ -550,18 +571,19 @@ size_t copy_to_iter(const void *addr, size_t bytes, struct iov_iter *i)
return bytes; return bytes;
} }
EXPORT_SYMBOL(copy_to_iter); EXPORT_SYMBOL(_copy_to_iter);
size_t copy_from_iter(void *addr, size_t bytes, struct iov_iter *i) size_t _copy_from_iter(void *addr, size_t bytes, struct iov_iter *i)
{ {
char *to = addr; char *to = addr;
if (unlikely(i->type & ITER_PIPE)) { if (unlikely(i->type & ITER_PIPE)) {
WARN_ON(1); WARN_ON(1);
return 0; return 0;
} }
if (iter_is_iovec(i))
might_fault();
iterate_and_advance(i, bytes, v, iterate_and_advance(i, bytes, v,
__copy_from_user((to += v.iov_len) - v.iov_len, v.iov_base, copyin((to += v.iov_len) - v.iov_len, v.iov_base, v.iov_len),
v.iov_len),
memcpy_from_page((to += v.bv_len) - v.bv_len, v.bv_page, memcpy_from_page((to += v.bv_len) - v.bv_len, v.bv_page,
v.bv_offset, v.bv_len), v.bv_offset, v.bv_len),
memcpy((to += v.iov_len) - v.iov_len, v.iov_base, v.iov_len) memcpy((to += v.iov_len) - v.iov_len, v.iov_base, v.iov_len)
...@@ -569,9 +591,9 @@ size_t copy_from_iter(void *addr, size_t bytes, struct iov_iter *i) ...@@ -569,9 +591,9 @@ size_t copy_from_iter(void *addr, size_t bytes, struct iov_iter *i)
return bytes; return bytes;
} }
EXPORT_SYMBOL(copy_from_iter); EXPORT_SYMBOL(_copy_from_iter);
bool copy_from_iter_full(void *addr, size_t bytes, struct iov_iter *i) bool _copy_from_iter_full(void *addr, size_t bytes, struct iov_iter *i)
{ {
char *to = addr; char *to = addr;
if (unlikely(i->type & ITER_PIPE)) { if (unlikely(i->type & ITER_PIPE)) {
...@@ -581,8 +603,10 @@ bool copy_from_iter_full(void *addr, size_t bytes, struct iov_iter *i) ...@@ -581,8 +603,10 @@ bool copy_from_iter_full(void *addr, size_t bytes, struct iov_iter *i)
if (unlikely(i->count < bytes)) if (unlikely(i->count < bytes))
return false; return false;
if (iter_is_iovec(i))
might_fault();
iterate_all_kinds(i, bytes, v, ({ iterate_all_kinds(i, bytes, v, ({
if (__copy_from_user((to += v.iov_len) - v.iov_len, if (copyin((to += v.iov_len) - v.iov_len,
v.iov_base, v.iov_len)) v.iov_base, v.iov_len))
return false; return false;
0;}), 0;}),
...@@ -594,9 +618,9 @@ bool copy_from_iter_full(void *addr, size_t bytes, struct iov_iter *i) ...@@ -594,9 +618,9 @@ bool copy_from_iter_full(void *addr, size_t bytes, struct iov_iter *i)
iov_iter_advance(i, bytes); iov_iter_advance(i, bytes);
return true; return true;
} }
EXPORT_SYMBOL(copy_from_iter_full); EXPORT_SYMBOL(_copy_from_iter_full);
size_t copy_from_iter_nocache(void *addr, size_t bytes, struct iov_iter *i) size_t _copy_from_iter_nocache(void *addr, size_t bytes, struct iov_iter *i)
{ {
char *to = addr; char *to = addr;
if (unlikely(i->type & ITER_PIPE)) { if (unlikely(i->type & ITER_PIPE)) {
...@@ -613,10 +637,10 @@ size_t copy_from_iter_nocache(void *addr, size_t bytes, struct iov_iter *i) ...@@ -613,10 +637,10 @@ size_t copy_from_iter_nocache(void *addr, size_t bytes, struct iov_iter *i)
return bytes; return bytes;
} }
EXPORT_SYMBOL(copy_from_iter_nocache); EXPORT_SYMBOL(_copy_from_iter_nocache);
#ifdef CONFIG_ARCH_HAS_UACCESS_FLUSHCACHE #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; char *to = addr;
if (unlikely(i->type & ITER_PIPE)) { if (unlikely(i->type & ITER_PIPE)) {
...@@ -634,10 +658,10 @@ size_t copy_from_iter_flushcache(void *addr, size_t bytes, struct iov_iter *i) ...@@ -634,10 +658,10 @@ size_t copy_from_iter_flushcache(void *addr, size_t bytes, struct iov_iter *i)
return bytes; return bytes;
} }
EXPORT_SYMBOL_GPL(copy_from_iter_flushcache); EXPORT_SYMBOL_GPL(_copy_from_iter_flushcache);
#endif #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; char *to = addr;
if (unlikely(i->type & ITER_PIPE)) { if (unlikely(i->type & ITER_PIPE)) {
...@@ -659,11 +683,22 @@ bool copy_from_iter_full_nocache(void *addr, size_t bytes, struct iov_iter *i) ...@@ -659,11 +683,22 @@ bool copy_from_iter_full_nocache(void *addr, size_t bytes, struct iov_iter *i)
iov_iter_advance(i, bytes); iov_iter_advance(i, bytes);
return true; return true;
} }
EXPORT_SYMBOL(copy_from_iter_full_nocache); EXPORT_SYMBOL(_copy_from_iter_full_nocache);
static inline bool page_copy_sane(struct page *page, size_t offset, size_t n)
{
size_t v = n + offset;
if (likely(n <= v && v <= (PAGE_SIZE << compound_order(page))))
return true;
WARN_ON(1);
return false;
}
size_t copy_page_to_iter(struct page *page, size_t offset, size_t bytes, size_t copy_page_to_iter(struct page *page, size_t offset, size_t bytes,
struct iov_iter *i) struct iov_iter *i)
{ {
if (unlikely(!page_copy_sane(page, offset, bytes)))
return 0;
if (i->type & (ITER_BVEC|ITER_KVEC)) { if (i->type & (ITER_BVEC|ITER_KVEC)) {
void *kaddr = kmap_atomic(page); void *kaddr = kmap_atomic(page);
size_t wanted = copy_to_iter(kaddr + offset, bytes, i); size_t wanted = copy_to_iter(kaddr + offset, bytes, i);
...@@ -679,13 +714,15 @@ EXPORT_SYMBOL(copy_page_to_iter); ...@@ -679,13 +714,15 @@ EXPORT_SYMBOL(copy_page_to_iter);
size_t copy_page_from_iter(struct page *page, size_t offset, size_t bytes, size_t copy_page_from_iter(struct page *page, size_t offset, size_t bytes,
struct iov_iter *i) struct iov_iter *i)
{ {
if (unlikely(!page_copy_sane(page, offset, bytes)))
return 0;
if (unlikely(i->type & ITER_PIPE)) { if (unlikely(i->type & ITER_PIPE)) {
WARN_ON(1); WARN_ON(1);
return 0; return 0;
} }
if (i->type & (ITER_BVEC|ITER_KVEC)) { if (i->type & (ITER_BVEC|ITER_KVEC)) {
void *kaddr = kmap_atomic(page); void *kaddr = kmap_atomic(page);
size_t wanted = copy_from_iter(kaddr + offset, bytes, i); size_t wanted = _copy_from_iter(kaddr + offset, bytes, i);
kunmap_atomic(kaddr); kunmap_atomic(kaddr);
return wanted; return wanted;
} else } else
...@@ -722,7 +759,7 @@ size_t iov_iter_zero(size_t bytes, struct iov_iter *i) ...@@ -722,7 +759,7 @@ size_t iov_iter_zero(size_t bytes, struct iov_iter *i)
if (unlikely(i->type & ITER_PIPE)) if (unlikely(i->type & ITER_PIPE))
return pipe_zero(bytes, i); return pipe_zero(bytes, i);
iterate_and_advance(i, bytes, v, iterate_and_advance(i, bytes, v,
__clear_user(v.iov_base, v.iov_len), clear_user(v.iov_base, v.iov_len),
memzero_page(v.bv_page, v.bv_offset, v.bv_len), memzero_page(v.bv_page, v.bv_offset, v.bv_len),
memset(v.iov_base, 0, v.iov_len) memset(v.iov_base, 0, v.iov_len)
) )
...@@ -735,14 +772,17 @@ size_t iov_iter_copy_from_user_atomic(struct page *page, ...@@ -735,14 +772,17 @@ size_t iov_iter_copy_from_user_atomic(struct page *page,
struct iov_iter *i, unsigned long offset, size_t bytes) struct iov_iter *i, unsigned long offset, size_t bytes)
{ {
char *kaddr = kmap_atomic(page), *p = kaddr + offset; char *kaddr = kmap_atomic(page), *p = kaddr + offset;
if (unlikely(!page_copy_sane(page, offset, bytes))) {
kunmap_atomic(kaddr);
return 0;
}
if (unlikely(i->type & ITER_PIPE)) { if (unlikely(i->type & ITER_PIPE)) {
kunmap_atomic(kaddr); kunmap_atomic(kaddr);
WARN_ON(1); WARN_ON(1);
return 0; return 0;
} }
iterate_all_kinds(i, bytes, v, iterate_all_kinds(i, bytes, v,
__copy_from_user_inatomic((p += v.iov_len) - v.iov_len, copyin((p += v.iov_len) - v.iov_len, v.iov_base, v.iov_len),
v.iov_base, v.iov_len),
memcpy_from_page((p += v.bv_len) - v.bv_len, v.bv_page, memcpy_from_page((p += v.bv_len) - v.bv_len, v.bv_page,
v.bv_offset, v.bv_len), v.bv_offset, v.bv_len),
memcpy((p += v.iov_len) - v.iov_len, v.iov_base, v.iov_len) memcpy((p += v.iov_len) - v.iov_len, v.iov_base, v.iov_len)
......
...@@ -6,8 +6,11 @@ ...@@ -6,8 +6,11 @@
unsigned long _copy_from_user(void *to, const void __user *from, unsigned long n) unsigned long _copy_from_user(void *to, const void __user *from, unsigned long n)
{ {
unsigned long res = n; unsigned long res = n;
if (likely(access_ok(VERIFY_READ, from, n))) might_fault();
if (likely(access_ok(VERIFY_READ, from, n))) {
kasan_check_write(to, n);
res = raw_copy_from_user(to, from, n); res = raw_copy_from_user(to, from, n);
}
if (unlikely(res)) if (unlikely(res))
memset(to + (n - res), 0, res); memset(to + (n - res), 0, res);
return res; return res;
...@@ -18,8 +21,11 @@ EXPORT_SYMBOL(_copy_from_user); ...@@ -18,8 +21,11 @@ EXPORT_SYMBOL(_copy_from_user);
#ifndef INLINE_COPY_TO_USER #ifndef INLINE_COPY_TO_USER
unsigned long _copy_to_user(void *to, const void __user *from, unsigned long n) unsigned long _copy_to_user(void *to, const void __user *from, unsigned long n)
{ {
if (likely(access_ok(VERIFY_WRITE, to, n))) might_fault();
if (likely(access_ok(VERIFY_WRITE, to, n))) {
kasan_check_read(from, n);
n = raw_copy_to_user(to, from, n); n = raw_copy_to_user(to, from, n);
}
return n; return n;
} }
EXPORT_SYMBOL(_copy_to_user); EXPORT_SYMBOL(_copy_to_user);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment