提交 9f06cdd2 编写于 作者: N Nicholas Piggin 提交者: Yang Yingliang

powerpc/uaccess: Evaluate macro arguments once, before user access is allowed

stable inclusion
from linux-4.19.159
commit 6b36099dc92354dc0a895115605d4bf31f252142
CVE: CVE-2020-4788

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

commit d02f6b7d upstream.

get/put_user() can be called with nontrivial arguments. fs/proc/page.c
has a good example:

    if (put_user(stable_page_flags(ppage), out)) {

stable_page_flags() is quite a lot of code, including spin locks in
the page allocator.

Ensure these arguments are evaluated before user access is allowed.

This improves security by reducing code with access to userspace, but
it also fixes a PREEMPT bug with KUAP on powerpc/64s:
stable_page_flags() is currently called with AMR set to allow writes,
it ends up calling spin_unlock(), which can call preempt_schedule. But
the task switch code can not be called with AMR set (it relies on
interrupts saving the register), so this blows up.

It's fine if the code inside allow_user_access() is preemptible,
because a timer or IPI will save the AMR, but it's not okay to
explicitly cause a reschedule.

Fixes: de78a9c4 ("powerpc: Add a framework for Kernel Userspace Access Protection")
Signed-off-by: NNicholas Piggin <npiggin@gmail.com>
Signed-off-by: NMichael Ellerman <mpe@ellerman.id.au>
Link: https://lore.kernel.org/r/20200407041245.600651-1-npiggin@gmail.comSigned-off-by: NDaniel Axtens <dja@axtens.net>
Signed-off-by: NGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Conflicts:
  arch/powerpc/include/asm/uaccess.h
[yyl: 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>
上级 79339fe9
...@@ -167,13 +167,17 @@ do { \ ...@@ -167,13 +167,17 @@ do { \
({ \ ({ \
long __pu_err; \ long __pu_err; \
__typeof__(*(ptr)) __user *__pu_addr = (ptr); \ __typeof__(*(ptr)) __user *__pu_addr = (ptr); \
__typeof__(*(ptr)) __pu_val = (x); \
__typeof__(size) __pu_size = (size); \
\
if (!is_kernel_addr((unsigned long)__pu_addr)) \ if (!is_kernel_addr((unsigned long)__pu_addr)) \
might_fault(); \ might_fault(); \
__chk_user_ptr(ptr); \ __chk_user_ptr(__pu_addr); \
if (do_allow) \ if (do_allow) \
__put_user_size((x), __pu_addr, (size), __pu_err); \ __put_user_size(__pu_val, __pu_addr, __pu_size, __pu_err); \
else \ else \
__put_user_size_allowed((x), __pu_addr, (size), __pu_err); \ __put_user_size_allowed(__pu_val, __pu_addr, __pu_size, __pu_err); \
\
__pu_err; \ __pu_err; \
}) })
...@@ -181,9 +185,13 @@ do { \ ...@@ -181,9 +185,13 @@ do { \
({ \ ({ \
long __pu_err = -EFAULT; \ long __pu_err = -EFAULT; \
__typeof__(*(ptr)) __user *__pu_addr = (ptr); \ __typeof__(*(ptr)) __user *__pu_addr = (ptr); \
__typeof__(*(ptr)) __pu_val = (x); \
__typeof__(size) __pu_size = (size); \
\
might_fault(); \ might_fault(); \
if (access_ok(__pu_addr, size)) \ if (access_ok(__pu_addr, __pu_size)) \
__put_user_size((x), __pu_addr, (size), __pu_err); \ __put_user_size(__pu_val, __pu_addr, __pu_size, __pu_err); \
\
__pu_err; \ __pu_err; \
}) })
...@@ -191,8 +199,12 @@ do { \ ...@@ -191,8 +199,12 @@ do { \
({ \ ({ \
long __pu_err; \ long __pu_err; \
__typeof__(*(ptr)) __user *__pu_addr = (ptr); \ __typeof__(*(ptr)) __user *__pu_addr = (ptr); \
__chk_user_ptr(ptr); \ __typeof__(*(ptr)) __pu_val = (x); \
__put_user_size((x), __pu_addr, (size), __pu_err); \ __typeof__(size) __pu_size = (size); \
\
__chk_user_ptr(__pu_addr); \
__put_user_size(__pu_val, __pu_addr, __pu_size, __pu_err); \
\
__pu_err; \ __pu_err; \
}) })
...@@ -284,15 +296,18 @@ do { \ ...@@ -284,15 +296,18 @@ do { \
long __gu_err; \ long __gu_err; \
__long_type(*(ptr)) __gu_val; \ __long_type(*(ptr)) __gu_val; \
__typeof__(*(ptr)) __user *__gu_addr = (ptr); \ __typeof__(*(ptr)) __user *__gu_addr = (ptr); \
__chk_user_ptr(ptr); \ __typeof__(size) __gu_size = (size); \
\
__chk_user_ptr(__gu_addr); \
if (!is_kernel_addr((unsigned long)__gu_addr)) \ if (!is_kernel_addr((unsigned long)__gu_addr)) \
might_fault(); \ might_fault(); \
barrier_nospec(); \ barrier_nospec(); \
if (do_allow) \ if (do_allow) \
__get_user_size(__gu_val, __gu_addr, (size), __gu_err); \ __get_user_size(__gu_val, __gu_addr, __gu_size, __gu_err); \
else \ else \
__get_user_size_allowed(__gu_val, __gu_addr, (size), __gu_err); \ __get_user_size_allowed(__gu_val, __gu_addr, __gu_size, __gu_err); \
(x) = (__typeof__(*(ptr)))__gu_val; \ (x) = (__typeof__(*(ptr)))__gu_val; \
\
__gu_err; \ __gu_err; \
}) })
...@@ -301,12 +316,15 @@ do { \ ...@@ -301,12 +316,15 @@ do { \
long __gu_err = -EFAULT; \ long __gu_err = -EFAULT; \
__long_type(*(ptr)) __gu_val = 0; \ __long_type(*(ptr)) __gu_val = 0; \
__typeof__(*(ptr)) __user *__gu_addr = (ptr); \ __typeof__(*(ptr)) __user *__gu_addr = (ptr); \
__typeof__(size) __gu_size = (size); \
\
might_fault(); \ might_fault(); \
if (access_ok(__gu_addr, (size))) { \ if (access_ok(__gu_addr, __gu_size)) { \
barrier_nospec(); \ barrier_nospec(); \
__get_user_size(__gu_val, __gu_addr, (size), __gu_err); \ __get_user_size(__gu_val, __gu_addr, __gu_size, __gu_err); \
} \ } \
(x) = (__force __typeof__(*(ptr)))__gu_val; \ (x) = (__force __typeof__(*(ptr)))__gu_val; \
\
__gu_err; \ __gu_err; \
}) })
...@@ -315,10 +333,13 @@ do { \ ...@@ -315,10 +333,13 @@ do { \
long __gu_err; \ long __gu_err; \
__long_type(*(ptr)) __gu_val; \ __long_type(*(ptr)) __gu_val; \
__typeof__(*(ptr)) __user *__gu_addr = (ptr); \ __typeof__(*(ptr)) __user *__gu_addr = (ptr); \
__chk_user_ptr(ptr); \ __typeof__(size) __gu_size = (size); \
\
__chk_user_ptr(__gu_addr); \
barrier_nospec(); \ barrier_nospec(); \
__get_user_size(__gu_val, __gu_addr, (size), __gu_err); \ __get_user_size(__gu_val, __gu_addr, __gu_size, __gu_err); \
(x) = (__force __typeof__(*(ptr)))__gu_val; \ (x) = (__force __typeof__(*(ptr)))__gu_val; \
\
__gu_err; \ __gu_err; \
}) })
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册