提交 9f59cc51 编写于 作者: C Christophe Leroy 提交者: Yang Yingliang

powerpc: Add a framework for user access tracking

stable inclusion
from linux-4.19.159
commit 357a5e6febe8839bd217121baa394b11e8fe4085
CVE: CVE-2020-4788

--------------------------------

Backported from commit de78a9c4 ("powerpc: Add a framework
for Kernel Userspace Access Protection"). Here we don't try to
add the KUAP framework, we just want the helper functions
because we want to put uaccess flush helpers in them.

In terms of fixes, we don't need commit 1d8f739b ("powerpc/kuap:
Fix set direction in allow/prevent_user_access()") as we don't have
real KUAP. Likewise as all our allows are noops and all our prevents
are just flushes, we don't need commit 9dc086f1 ("powerpc/futex:
Fix incorrect user access blocking") The other 2 fixes we do need.

The original description is:

This patch implements a framework for Kernel Userspace Access
Protection.

Then subarches will have the possibility to provide their own
implementation by providing setup_kuap() and
allow/prevent_user_access().

Some platforms will need to know the area accessed and whether it is
accessed from read, write or both. Therefore source, destination and
size and handed over to the two functions.

mpe: Rename to allow/prevent rather than unlock/lock, and add
read/write wrappers. Drop the 32-bit code for now until we have an
implementation for it. Add kuap to pt_regs for 64-bit as well as
32-bit. Don't split strings, use pr_crit_ratelimited().
Signed-off-by: NChristophe Leroy <christophe.leroy@c-s.fr>
Signed-off-by: NRussell Currey <ruscur@russell.cc>
Signed-off-by: NMichael Ellerman <mpe@ellerman.id.au>
Signed-off-by: NDaniel Axtens <dja@axtens.net>
Signed-off-by: NGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Conflicts:
  arch/powerpc/include/asm/uaccess.h
  arch/powerpc/include/asm/futex.h
[yyl: resolve the conflits, because VERIFY_WRITE is already removed]
Signed-off-by: NYang Yingliang <yangyingliang@huawei.com>
Reviewed-by: NJason Yan <yanaijie@huawei.com>
Signed-off-by: NYang Yingliang <yangyingliang@huawei.com>
上级 af0a781e
...@@ -35,6 +35,7 @@ static inline int arch_futex_atomic_op_inuser(int op, int oparg, int *oval, ...@@ -35,6 +35,7 @@ static inline int arch_futex_atomic_op_inuser(int op, int oparg, int *oval,
{ {
int oldval = 0, ret; int oldval = 0, ret;
allow_write_to_user(uaddr, sizeof(*uaddr));
pagefault_disable(); pagefault_disable();
switch (op) { switch (op) {
...@@ -61,6 +62,7 @@ static inline int arch_futex_atomic_op_inuser(int op, int oparg, int *oval, ...@@ -61,6 +62,7 @@ static inline int arch_futex_atomic_op_inuser(int op, int oparg, int *oval,
*oval = oldval; *oval = oldval;
prevent_write_to_user(uaddr, sizeof(*uaddr));
return ret; return ret;
} }
...@@ -74,6 +76,7 @@ futex_atomic_cmpxchg_inatomic(u32 *uval, u32 __user *uaddr, ...@@ -74,6 +76,7 @@ futex_atomic_cmpxchg_inatomic(u32 *uval, u32 __user *uaddr,
if (!access_ok(uaddr, sizeof(u32))) if (!access_ok(uaddr, sizeof(u32)))
return -EFAULT; return -EFAULT;
allow_write_to_user(uaddr, sizeof(*uaddr));
__asm__ __volatile__ ( __asm__ __volatile__ (
PPC_ATOMIC_ENTRY_BARRIER PPC_ATOMIC_ENTRY_BARRIER
"1: lwarx %1,0,%3 # futex_atomic_cmpxchg_inatomic\n\ "1: lwarx %1,0,%3 # futex_atomic_cmpxchg_inatomic\n\
...@@ -94,6 +97,7 @@ futex_atomic_cmpxchg_inatomic(u32 *uval, u32 __user *uaddr, ...@@ -94,6 +97,7 @@ futex_atomic_cmpxchg_inatomic(u32 *uval, u32 __user *uaddr,
: "cc", "memory"); : "cc", "memory");
*uval = prev; *uval = prev;
prevent_write_to_user(uaddr, sizeof(*uaddr));
return ret; return ret;
} }
......
/* SPDX-License-Identifier: GPL-2.0 */
#ifndef _ASM_POWERPC_KUP_H_
#define _ASM_POWERPC_KUP_H_
#ifndef __ASSEMBLY__
#include <asm/pgtable.h>
static inline void allow_user_access(void __user *to, const void __user *from,
unsigned long size) { }
static inline void prevent_user_access(void __user *to, const void __user *from,
unsigned long size) { }
static inline void allow_read_from_user(const void __user *from, unsigned long size)
{
allow_user_access(NULL, from, size);
}
static inline void allow_write_to_user(void __user *to, unsigned long size)
{
allow_user_access(to, NULL, size);
}
static inline void prevent_read_from_user(const void __user *from, unsigned long size)
{
prevent_user_access(NULL, from, size);
}
static inline void prevent_write_to_user(void __user *to, unsigned long size)
{
prevent_user_access(to, NULL, size);
}
#endif /* !__ASSEMBLY__ */
#endif /* _ASM_POWERPC_KUP_H_ */
...@@ -6,6 +6,7 @@ ...@@ -6,6 +6,7 @@
#include <asm/processor.h> #include <asm/processor.h>
#include <asm/page.h> #include <asm/page.h>
#include <asm/extable.h> #include <asm/extable.h>
#include <asm/kup.h>
/* /*
* The fs value determines whether argument validity checking should be * The fs value determines whether argument validity checking should be
...@@ -141,6 +142,7 @@ extern long __put_user_bad(void); ...@@ -141,6 +142,7 @@ extern long __put_user_bad(void);
#define __put_user_size(x, ptr, size, retval) \ #define __put_user_size(x, ptr, size, retval) \
do { \ do { \
retval = 0; \ retval = 0; \
allow_write_to_user(ptr, size); \
switch (size) { \ switch (size) { \
case 1: __put_user_asm(x, ptr, retval, "stb"); break; \ case 1: __put_user_asm(x, ptr, retval, "stb"); break; \
case 2: __put_user_asm(x, ptr, retval, "sth"); break; \ case 2: __put_user_asm(x, ptr, retval, "sth"); break; \
...@@ -148,6 +150,7 @@ do { \ ...@@ -148,6 +150,7 @@ do { \
case 8: __put_user_asm2(x, ptr, retval); break; \ case 8: __put_user_asm2(x, ptr, retval); break; \
default: __put_user_bad(); \ default: __put_user_bad(); \
} \ } \
prevent_write_to_user(ptr, size); \
} while (0) } while (0)
#define __put_user_nocheck(x, ptr, size) \ #define __put_user_nocheck(x, ptr, size) \
...@@ -240,6 +243,7 @@ do { \ ...@@ -240,6 +243,7 @@ do { \
__chk_user_ptr(ptr); \ __chk_user_ptr(ptr); \
if (size > sizeof(x)) \ if (size > sizeof(x)) \
(x) = __get_user_bad(); \ (x) = __get_user_bad(); \
allow_read_from_user(ptr, size); \
switch (size) { \ switch (size) { \
case 1: __get_user_asm(x, ptr, retval, "lbz"); break; \ case 1: __get_user_asm(x, ptr, retval, "lbz"); break; \
case 2: __get_user_asm(x, ptr, retval, "lhz"); break; \ case 2: __get_user_asm(x, ptr, retval, "lhz"); break; \
...@@ -247,6 +251,7 @@ do { \ ...@@ -247,6 +251,7 @@ do { \
case 8: __get_user_asm2(x, ptr, retval); break; \ case 8: __get_user_asm2(x, ptr, retval); break; \
default: (x) = __get_user_bad(); \ default: (x) = __get_user_bad(); \
} \ } \
prevent_read_from_user(ptr, size); \
} while (0) } while (0)
/* /*
...@@ -306,16 +311,22 @@ extern unsigned long __copy_tofrom_user(void __user *to, ...@@ -306,16 +311,22 @@ extern unsigned long __copy_tofrom_user(void __user *to,
static inline unsigned long static inline unsigned long
raw_copy_in_user(void __user *to, const void __user *from, unsigned long n) raw_copy_in_user(void __user *to, const void __user *from, unsigned long n)
{ {
unsigned long ret;
barrier_nospec(); barrier_nospec();
return __copy_tofrom_user(to, from, n); allow_user_access(to, from, n);
ret = __copy_tofrom_user(to, from, n);
prevent_user_access(to, from, n);
return ret;
} }
#endif /* __powerpc64__ */ #endif /* __powerpc64__ */
static inline unsigned long raw_copy_from_user(void *to, static inline unsigned long raw_copy_from_user(void *to,
const void __user *from, unsigned long n) const void __user *from, unsigned long n)
{ {
unsigned long ret;
if (__builtin_constant_p(n) && (n <= 8)) { if (__builtin_constant_p(n) && (n <= 8)) {
unsigned long ret = 1; ret = 1;
switch (n) { switch (n) {
case 1: case 1:
...@@ -340,14 +351,18 @@ static inline unsigned long raw_copy_from_user(void *to, ...@@ -340,14 +351,18 @@ static inline unsigned long raw_copy_from_user(void *to,
} }
barrier_nospec(); barrier_nospec();
return __copy_tofrom_user((__force void __user *)to, from, n); allow_read_from_user(from, n);
ret = __copy_tofrom_user((__force void __user *)to, from, n);
prevent_read_from_user(from, n);
return ret;
} }
static inline unsigned long raw_copy_to_user(void __user *to, static inline unsigned long raw_copy_to_user(void __user *to,
const void *from, unsigned long n) const void *from, unsigned long n)
{ {
unsigned long ret;
if (__builtin_constant_p(n) && (n <= 8)) { if (__builtin_constant_p(n) && (n <= 8)) {
unsigned long ret = 1; ret = 1;
switch (n) { switch (n) {
case 1: case 1:
...@@ -367,17 +382,24 @@ static inline unsigned long raw_copy_to_user(void __user *to, ...@@ -367,17 +382,24 @@ static inline unsigned long raw_copy_to_user(void __user *to,
return 0; return 0;
} }
return __copy_tofrom_user(to, (__force const void __user *)from, n); allow_write_to_user(to, n);
ret = __copy_tofrom_user(to, (__force const void __user *)from, n);
prevent_write_to_user(to, n);
return ret;
} }
extern unsigned long __clear_user(void __user *addr, unsigned long size); extern unsigned long __clear_user(void __user *addr, unsigned long size);
static inline unsigned long clear_user(void __user *addr, unsigned long size) static inline unsigned long clear_user(void __user *addr, unsigned long size)
{ {
unsigned long ret = size;
might_fault(); might_fault();
if (likely(access_ok(addr, size))) if (likely(access_ok(VERIFY_WRITE, addr, size))) {
return __clear_user(addr, size); allow_write_to_user(addr, size);
return size; ret = __clear_user(addr, size);
prevent_write_to_user(addr, size);
}
return ret;
} }
extern long strncpy_from_user(char *dst, const char __user *src, long count); extern long strncpy_from_user(char *dst, const char __user *src, long count);
......
...@@ -29,6 +29,7 @@ __wsum csum_and_copy_from_user(const void __user *src, void *dst, ...@@ -29,6 +29,7 @@ __wsum csum_and_copy_from_user(const void __user *src, void *dst,
unsigned int csum; unsigned int csum;
might_sleep(); might_sleep();
allow_read_from_user(src, len);
*err_ptr = 0; *err_ptr = 0;
...@@ -60,6 +61,7 @@ __wsum csum_and_copy_from_user(const void __user *src, void *dst, ...@@ -60,6 +61,7 @@ __wsum csum_and_copy_from_user(const void __user *src, void *dst,
} }
out: out:
prevent_read_from_user(src, len);
return (__force __wsum)csum; return (__force __wsum)csum;
} }
EXPORT_SYMBOL(csum_and_copy_from_user); EXPORT_SYMBOL(csum_and_copy_from_user);
...@@ -70,6 +72,7 @@ __wsum csum_and_copy_to_user(const void *src, void __user *dst, int len, ...@@ -70,6 +72,7 @@ __wsum csum_and_copy_to_user(const void *src, void __user *dst, int len,
unsigned int csum; unsigned int csum;
might_sleep(); might_sleep();
allow_write_to_user(dst, len);
*err_ptr = 0; *err_ptr = 0;
...@@ -97,6 +100,7 @@ __wsum csum_and_copy_to_user(const void *src, void __user *dst, int len, ...@@ -97,6 +100,7 @@ __wsum csum_and_copy_to_user(const void *src, void __user *dst, int len,
} }
out: out:
prevent_write_to_user(dst, len);
return (__force __wsum)csum; return (__force __wsum)csum;
} }
EXPORT_SYMBOL(csum_and_copy_to_user); EXPORT_SYMBOL(csum_and_copy_to_user);
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册