Re: [PATCH v3 2/6] uaccess-buffer: add core code
From: Peter Collingbourne <hidden>
Date: 2021-12-09 22:14:25
Also in:
lkml
On Wed, Dec 8, 2021 at 8:46 AM Marco Elver [off-list ref] wrote:
On Tue, Dec 07, 2021 at 08:48PM -0800, Peter Collingbourne wrote:quoted
Add the core code to support uaccess logging. Subsequent patches will hook this up to the arch-specific kernel entry and exit code for certain architectures. Link: https://linux-review.googlesource.com/id/I6581765646501a5631b281d670903945ebadc57d Signed-off-by: Peter Collingbourne <redacted> --- v3: - performance optimizations for entry/exit code - don't use kcur == NULL to mean overflow - fix potential double free in clone() - don't allocate a new kernel-side uaccess buffer for each syscall - fix uaccess buffer leak on exit - fix some sparse warnings v2: - New interface that avoids multiple syscalls per real syscall and is arch-generic - Avoid logging uaccesses done by BPF programs - Add documentation - Split up into multiple patches - Various code moves, renames etc as requested by Marco fs/exec.c | 3 + include/linux/instrumented-uaccess.h | 6 +- include/linux/sched.h | 5 ++ include/linux/uaccess-buffer-info.h | 46 ++++++++++ include/linux/uaccess-buffer.h | 112 +++++++++++++++++++++++ include/uapi/linux/prctl.h | 3 + include/uapi/linux/uaccess-buffer.h | 27 ++++++ kernel/Makefile | 1 + kernel/bpf/helpers.c | 7 +- kernel/fork.c | 4 + kernel/signal.c | 4 +- kernel/sys.c | 6 ++ kernel/uaccess-buffer.c | 129 +++++++++++++++++++++++++++ 13 files changed, 350 insertions(+), 3 deletions(-) create mode 100644 include/linux/uaccess-buffer-info.h create mode 100644 include/linux/uaccess-buffer.h create mode 100644 include/uapi/linux/uaccess-buffer.h create mode 100644 kernel/uaccess-buffer.cdiff --git a/fs/exec.c b/fs/exec.c index 537d92c41105..c9975e790f30 100644 --- a/fs/exec.c +++ b/fs/exec.c@@ -65,6 +65,7 @@ #include <linux/vmalloc.h> #include <linux/io_uring.h> #include <linux/syscall_user_dispatch.h> +#include <linux/uaccess-buffer.h> #include <linux/uaccess.h> #include <asm/mmu_context.h>@@ -1313,6 +1314,8 @@ int begin_new_exec(struct linux_binprm * bprm) me->personality &= ~bprm->per_clear; clear_syscall_work_syscall_user_dispatch(me); + uaccess_buffer_set_descriptor_addr_addr(0); + uaccess_buffer_free(current); /* * We have to apply CLOEXEC before we change whether the process isdiff --git a/include/linux/instrumented-uaccess.h b/include/linux/instrumented-uaccess.h index ece549088e50..b967f4436d15 100644 --- a/include/linux/instrumented-uaccess.h +++ b/include/linux/instrumented-uaccess.h@@ -2,7 +2,8 @@ /* * This header provides generic wrappers for memory access instrumentation for - * uaccess routines that the compiler cannot emit for: KASAN, KCSAN. + * uaccess routines that the compiler cannot emit for: KASAN, KCSAN, + * uaccess buffers. */ #ifndef _LINUX_INSTRUMENTED_UACCESS_H #define _LINUX_INSTRUMENTED_UACCESS_H@@ -11,6 +12,7 @@ #include <linux/kasan-checks.h> #include <linux/kcsan-checks.h> #include <linux/types.h> +#include <linux/uaccess-buffer.h> /** * instrument_copy_to_user - instrument reads of copy_to_user@@ -27,6 +29,7 @@ instrument_copy_to_user(void __user *to, const void *from, unsigned long n) { kasan_check_read(from, n); kcsan_check_read(from, n); + uaccess_buffer_log_write(to, n); } /**@@ -44,6 +47,7 @@ instrument_copy_from_user(const void *to, const void __user *from, unsigned long { kasan_check_write(to, n); kcsan_check_write(to, n); + uaccess_buffer_log_read(from, n); } #endif /* _LINUX_INSTRUMENTED_UACCESS_H */diff --git a/include/linux/sched.h b/include/linux/sched.h index 78c351e35fec..7c5278d7b57d 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h@@ -34,6 +34,7 @@ #include <linux/rseq.h> #include <linux/seqlock.h> #include <linux/kcsan.h> +#include <linux/uaccess-buffer-info.h> #include <asm/kmap_size.h> /* task_struct member predeclarations (sorted alphabetically): */@@ -1484,6 +1485,10 @@ struct task_struct { struct callback_head l1d_flush_kill; #endif +#ifdef CONFIG_HAVE_ARCH_UACCESS_BUFFER + struct uaccess_buffer_info uaccess_buffer; +#endif + /* * New fields for task_struct should be added above here, so that * they are included in the randomized portion of task_struct.diff --git a/include/linux/uaccess-buffer-info.h b/include/linux/uaccess-buffer-info.h new file mode 100644 index 000000000000..15e2d8f7c074 --- /dev/null +++ b/include/linux/uaccess-buffer-info.h@@ -0,0 +1,46 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _LINUX_UACCESS_BUFFER_INFO_H +#define _LINUX_UACCESS_BUFFER_INFO_H + +#ifdef CONFIG_HAVE_ARCH_UACCESS_BUFFER + +struct uaccess_buffer_info { + /* + * The pointer to pointer to struct uaccess_descriptor. This is the + * value controlled by prctl(PR_SET_UACCESS_DESCRIPTOR_ADDR_ADDR). + */ + struct uaccess_descriptor __user *__user *desc_ptr_ptr; + + /* + * The pointer to struct uaccess_descriptor read at syscall entry time. + */ + struct uaccess_descriptor __user *desc_ptr; + + /* + * A pointer to the kernel's temporary copy of the uaccess log for the + * current syscall. We log to a kernel buffer in order to avoid leaking + * timing information to userspace. + */ + struct uaccess_buffer_entry *kbegin; + + /* + * The position of the next uaccess buffer entry for the current + * syscall, or NULL if we are not logging the current syscall. + */ + struct uaccess_buffer_entry *kcur; + + /* + * A pointer to the end of the kernel's uaccess log. + */ + struct uaccess_buffer_entry *kend; + + /* + * The pointer to the userspace uaccess log, as read from the + * struct uaccess_descriptor. + */ + struct uaccess_buffer_entry __user *ubegin; +}; + +#endif + +#endif /* _LINUX_UACCESS_BUFFER_INFO_H */diff --git a/include/linux/uaccess-buffer.h b/include/linux/uaccess-buffer.h new file mode 100644 index 000000000000..f2f46db274f3 --- /dev/null +++ b/include/linux/uaccess-buffer.h@@ -0,0 +1,112 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _LINUX_UACCESS_BUFFER_H +#define _LINUX_UACCESS_BUFFER_H + +#include <linux/sched.h> +#include <uapi/linux/uaccess-buffer.h> + +#include <asm-generic/errno-base.h> + +#ifdef CONFIG_HAVE_ARCH_UACCESS_BUFFERKernel-doc comments for each of the below would be useful (if __ prefixed functions are implementation details, they can be left as-is).
Done.
quoted
+ +static inline bool uaccess_buffer_maybe_blocked(struct task_struct *tsk) +{ + return test_task_syscall_work(tsk, UACCESS_BUFFER_ENTRY); +} + +void __uaccess_buffer_syscall_entry(void); +static inline void uaccess_buffer_syscall_entry(void) +{ + __uaccess_buffer_syscall_entry(); +} + +void __uaccess_buffer_syscall_exit(void); +static inline void uaccess_buffer_syscall_exit(void) +{ + __uaccess_buffer_syscall_exit(); +} + +bool __uaccess_buffer_pre_exit_loop(void); +static inline bool uaccess_buffer_pre_exit_loop(void) +{ + if (!test_syscall_work(UACCESS_BUFFER_ENTRY)) + return false; + return __uaccess_buffer_pre_exit_loop(); +} + +void __uaccess_buffer_post_exit_loop(void); +static inline void uaccess_buffer_post_exit_loop(bool pending) +{ + if (pending) + __uaccess_buffer_post_exit_loop(); +} + +static inline int uaccess_buffer_set_descriptor_addr_addr(unsigned long addr) +{ + current->uaccess_buffer.desc_ptr_ptr = + (struct uaccess_descriptor __user * __user *)addr; + if (addr) + set_syscall_work(UACCESS_BUFFER_ENTRY); + else + clear_syscall_work(UACCESS_BUFFER_ENTRY); + return 0; +} + +size_t copy_from_user_nolog(void *to, const void __user *from, size_t len);copy_from_user() has unsigned long for @len and also return type. I'd make them match.
Done.
quoted
+ +void uaccess_buffer_free(struct task_struct *tsk); + +void __uaccess_buffer_log_read(const void __user *from, unsigned long n); +static inline void uaccess_buffer_log_read(const void __user *from, unsigned long n) +{ + if (unlikely(test_syscall_work(UACCESS_BUFFER_EXIT))) + __uaccess_buffer_log_read(from, n); +} + +void __uaccess_buffer_log_write(void __user *to, unsigned long n); +static inline void uaccess_buffer_log_write(void __user *to, unsigned long n) +{ + if (unlikely(test_syscall_work(UACCESS_BUFFER_EXIT))) + __uaccess_buffer_log_write(to, n); +} + +#else + +static inline bool uaccess_buffer_maybe_blocked(struct task_struct *tsk) +{ + return false; +} +static inline void uaccess_buffer_syscall_entry(void) +{ +} +static inline void uaccess_buffer_syscall_exit(void) +{ +} +static inline bool uaccess_buffer_pre_exit_loop(void) +{ + return false; +} +static inline void uaccess_buffer_post_exit_loop(bool pending) +{ +} +static inline int uaccess_buffer_set_descriptor_addr_addr(unsigned long addr) +{ + return -EINVAL; +} +static inline void uaccess_buffer_free(struct task_struct *tsk) +{ +} + +#define copy_from_user_nolog(to, from, len) copy_from_user(to, from, len)"#define copy_from_user_nolog copy_from_user" ?
Done. Peter _______________________________________________ linux-arm-kernel mailing list linux-arm-kernel@lists.infradead.org http://lists.infradead.org/mailman/listinfo/linux-arm-kernel