| From foo@baz Thu Feb 8 03:32:24 CET 2018 |
| From: Dan Williams <dan.j.williams@intel.com> |
| Date: Mon, 29 Jan 2018 17:02:39 -0800 |
| Subject: x86: Introduce __uaccess_begin_nospec() and uaccess_try_nospec |
| |
| From: Dan Williams <dan.j.williams@intel.com> |
| |
| |
| (cherry picked from commit b3bbfb3fb5d25776b8e3f361d2eedaabb0b496cd) |
| |
| For __get_user() paths, do not allow the kernel to speculate on the value |
| of a user controlled pointer. In addition to the 'stac' instruction for |
| Supervisor Mode Access Protection (SMAP), a barrier_nospec() causes the |
| access_ok() result to resolve in the pipeline before the CPU might take any |
| speculative action on the pointer value. Given the cost of 'stac' the |
| speculation barrier is placed after 'stac' to hopefully overlap the cost of |
| disabling SMAP with the cost of flushing the instruction pipeline. |
| |
| Since __get_user is a major kernel interface that deals with user |
| controlled pointers, the __uaccess_begin_nospec() mechanism will prevent |
| speculative execution past an access_ok() permission check. While |
| speculative execution past access_ok() is not enough to lead to a kernel |
| memory leak, it is a necessary precondition. |
| |
| To be clear, __uaccess_begin_nospec() is addressing a class of potential |
| problems near __get_user() usages. |
| |
| Note, that while the barrier_nospec() in __uaccess_begin_nospec() is used |
| to protect __get_user(), pointer masking similar to array_index_nospec() |
| will be used for get_user() since it incorporates a bounds check near the |
| usage. |
| |
| uaccess_try_nospec provides the same mechanism for get_user_try. |
| |
| No functional changes. |
| |
| Suggested-by: Linus Torvalds <torvalds@linux-foundation.org> |
| Suggested-by: Andi Kleen <ak@linux.intel.com> |
| Suggested-by: Ingo Molnar <mingo@redhat.com> |
| Signed-off-by: Dan Williams <dan.j.williams@intel.com> |
| Signed-off-by: Thomas Gleixner <tglx@linutronix.de> |
| Cc: linux-arch@vger.kernel.org |
| Cc: Tom Lendacky <thomas.lendacky@amd.com> |
| Cc: Kees Cook <keescook@chromium.org> |
| Cc: kernel-hardening@lists.openwall.com |
| Cc: gregkh@linuxfoundation.org |
| Cc: Al Viro <viro@zeniv.linux.org.uk> |
| Cc: alan@linux.intel.com |
| Link: https://lkml.kernel.org/r/151727415922.33451.5796614273104346583.stgit@dwillia2-desk3.amr.corp.intel.com |
| Signed-off-by: David Woodhouse <dwmw@amazon.co.uk> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| --- |
| arch/x86/include/asm/uaccess.h | 9 +++++++++ |
| 1 file changed, 9 insertions(+) |
| |
| --- a/arch/x86/include/asm/uaccess.h |
| +++ b/arch/x86/include/asm/uaccess.h |
| @@ -123,6 +123,11 @@ extern int __get_user_bad(void); |
| |
| #define __uaccess_begin() stac() |
| #define __uaccess_end() clac() |
| +#define __uaccess_begin_nospec() \ |
| +({ \ |
| + stac(); \ |
| + barrier_nospec(); \ |
| +}) |
| |
| /* |
| * This is a type: either unsigned long, if the argument fits into |
| @@ -474,6 +479,10 @@ struct __large_struct { unsigned long bu |
| __uaccess_begin(); \ |
| barrier(); |
| |
| +#define uaccess_try_nospec do { \ |
| + current->thread.uaccess_err = 0; \ |
| + __uaccess_begin_nospec(); \ |
| + |
| #define uaccess_catch(err) \ |
| __uaccess_end(); \ |
| (err) |= (current->thread.uaccess_err ? -EFAULT : 0); \ |