mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-08-25 02:09:42 +00:00
![Linus Torvalds](/assets/img/avatar_default.png)
This reverts commit 9da3f2b740
.
It was well-intentioned, but wrong. Overriding the exception tables for
instructions for random reasons is just wrong, and that is what the new
code did.
It caused problems for tracing, and it caused problems for strncpy_from_user(),
because the new checks made perfectly valid use cases break, rather than
catch things that did bad things.
Unchecked user space accesses are a problem, but that's not a reason to
add invalid checks that then people have to work around with silly flags
(in this case, that 'kernel_uaccess_faults_ok' flag, which is just an
odd way to say "this commit was wrong" and was sprinked into random
places to hide the wrongness).
The real fix to unchecked user space accesses is to get rid of the
special "let's not check __get_user() and __put_user() at all" logic.
Make __{get|put}_user() be just aliases to the regular {get|put}_user()
functions, and make it impossible to access user space without having
the proper checks in places.
The raison d'être of the special double-underscore versions used to be
that the range check was expensive, and if you did multiple user
accesses, you'd do the range check up front (like the signal frame
handling code, for example). But SMAP (on x86) and PAN (on ARM) have
made that optimization pointless, because the _real_ expense is the "set
CPU flag to allow user space access".
Do let's not break the valid cases to catch invalid cases that shouldn't
even exist.
Cc: Thomas Gleixner <tglx@linutronix.de>
Cc: Kees Cook <keescook@chromium.org>
Cc: Tobin C. Harding <tobin@kernel.org>
Cc: Borislav Petkov <bp@alien8.de>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Andy Lutomirski <luto@kernel.org>
Cc: Jann Horn <jannh@google.com>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
107 lines
3.1 KiB
C
107 lines
3.1 KiB
C
/*
|
|
* Access kernel memory without faulting.
|
|
*/
|
|
#include <linux/export.h>
|
|
#include <linux/mm.h>
|
|
#include <linux/uaccess.h>
|
|
|
|
/**
|
|
* probe_kernel_read(): safely attempt to read from a location
|
|
* @dst: pointer to the buffer that shall take the data
|
|
* @src: address to read from
|
|
* @size: size of the data chunk
|
|
*
|
|
* Safely read from address @src to the buffer at @dst. If a kernel fault
|
|
* happens, handle that and return -EFAULT.
|
|
*
|
|
* We ensure that the copy_from_user is executed in atomic context so that
|
|
* do_page_fault() doesn't attempt to take mmap_sem. This makes
|
|
* probe_kernel_read() suitable for use within regions where the caller
|
|
* already holds mmap_sem, or other locks which nest inside mmap_sem.
|
|
*/
|
|
|
|
long __weak probe_kernel_read(void *dst, const void *src, size_t size)
|
|
__attribute__((alias("__probe_kernel_read")));
|
|
|
|
long __probe_kernel_read(void *dst, const void *src, size_t size)
|
|
{
|
|
long ret;
|
|
mm_segment_t old_fs = get_fs();
|
|
|
|
set_fs(KERNEL_DS);
|
|
pagefault_disable();
|
|
ret = __copy_from_user_inatomic(dst,
|
|
(__force const void __user *)src, size);
|
|
pagefault_enable();
|
|
set_fs(old_fs);
|
|
|
|
return ret ? -EFAULT : 0;
|
|
}
|
|
EXPORT_SYMBOL_GPL(probe_kernel_read);
|
|
|
|
/**
|
|
* probe_kernel_write(): safely attempt to write to a location
|
|
* @dst: address to write to
|
|
* @src: pointer to the data that shall be written
|
|
* @size: size of the data chunk
|
|
*
|
|
* Safely write to address @dst from the buffer at @src. If a kernel fault
|
|
* happens, handle that and return -EFAULT.
|
|
*/
|
|
long __weak probe_kernel_write(void *dst, const void *src, size_t size)
|
|
__attribute__((alias("__probe_kernel_write")));
|
|
|
|
long __probe_kernel_write(void *dst, const void *src, size_t size)
|
|
{
|
|
long ret;
|
|
mm_segment_t old_fs = get_fs();
|
|
|
|
set_fs(KERNEL_DS);
|
|
pagefault_disable();
|
|
ret = __copy_to_user_inatomic((__force void __user *)dst, src, size);
|
|
pagefault_enable();
|
|
set_fs(old_fs);
|
|
|
|
return ret ? -EFAULT : 0;
|
|
}
|
|
EXPORT_SYMBOL_GPL(probe_kernel_write);
|
|
|
|
/**
|
|
* strncpy_from_unsafe: - Copy a NUL terminated string from unsafe address.
|
|
* @dst: Destination address, in kernel space. This buffer must be at
|
|
* least @count bytes long.
|
|
* @unsafe_addr: Unsafe address.
|
|
* @count: Maximum number of bytes to copy, including the trailing NUL.
|
|
*
|
|
* Copies a NUL-terminated string from unsafe address to kernel buffer.
|
|
*
|
|
* On success, returns the length of the string INCLUDING the trailing NUL.
|
|
*
|
|
* If access fails, returns -EFAULT (some data may have been copied
|
|
* and the trailing NUL added).
|
|
*
|
|
* If @count is smaller than the length of the string, copies @count-1 bytes,
|
|
* sets the last byte of @dst buffer to NUL and returns @count.
|
|
*/
|
|
long strncpy_from_unsafe(char *dst, const void *unsafe_addr, long count)
|
|
{
|
|
mm_segment_t old_fs = get_fs();
|
|
const void *src = unsafe_addr;
|
|
long ret;
|
|
|
|
if (unlikely(count <= 0))
|
|
return 0;
|
|
|
|
set_fs(KERNEL_DS);
|
|
pagefault_disable();
|
|
|
|
do {
|
|
ret = __get_user(*dst++, (const char __user __force *)src++);
|
|
} while (dst[-1] && ret == 0 && src - unsafe_addr < count);
|
|
|
|
dst[-1] = '\0';
|
|
pagefault_enable();
|
|
set_fs(old_fs);
|
|
|
|
return ret ? -EFAULT : src - unsafe_addr;
|
|
}
|