uaccess: generalize access_ok()

There are many different ways that access_ok() is defined across
architectures, but in the end, they all just compare against the
user_addr_max() value or they accept anything.

Provide one definition that works for most architectures, checking
against TASK_SIZE_MAX for user processes or skipping the check inside
of uaccess_kernel() sections.

For architectures without CONFIG_SET_FS(), this should be the fastest
check, as it comes down to a single comparison of a pointer against a
compile-time constant, while the architecture specific versions tend to
do something more complex for historic reasons or get something wrong.

Type checking for __user annotations is handled inconsistently across
architectures, but this is easily simplified as well by using an inline
function that takes a 'const void __user *' argument. A handful of
callers need an extra __user annotation for this.

Some architectures had trick to use 33-bit or 65-bit arithmetic on the
addresses to calculate the overflow, however this simpler version uses
fewer registers, which means it can produce better object code in the
end despite needing a second (statically predicted) branch.

Reviewed-by: Christoph Hellwig <hch@lst.de>
Acked-by: Mark Rutland <mark.rutland@arm.com> [arm64, asm-generic]
Acked-by: Geert Uytterhoeven <geert@linux-m68k.org>
Acked-by: Stafford Horne <shorne@gmail.com>
Acked-by: Dinh Nguyen <dinguyen@kernel.org>
Signed-off-by: Arnd Bergmann <arnd@arndb.de>
This commit is contained in:
Arnd Bergmann
2022-02-15 17:55:04 +01:00
parent 23fc539e81
commit 12700c17fc
32 changed files with 110 additions and 362 deletions

View File

@@ -62,6 +62,7 @@ config SPARC32
config SPARC64
def_bool 64BIT
select ALTERNATE_USER_ADDRESS_SPACE
select HAVE_FUNCTION_TRACER
select HAVE_FUNCTION_GRAPH_TRACER
select HAVE_KRETPROBES

View File

@@ -10,9 +10,6 @@
#include <asm/uaccess_32.h>
#endif
#define user_addr_max() \
(uaccess_kernel() ? ~0UL : TASK_SIZE)
long strncpy_from_user(char *dest, const char __user *src, long count);
#endif

View File

@@ -25,17 +25,7 @@
#define get_fs() (current->thread.current_ds)
#define set_fs(val) ((current->thread.current_ds) = (val))
#define uaccess_kernel() (get_fs().seg == KERNEL_DS.seg)
/* We have there a nice not-mapped page at PAGE_OFFSET - PAGE_SIZE, so that this test
* can be fairly lightweight.
* No one can read/write anything from userland in the kernel space by setting
* large size and address near to PAGE_OFFSET - a fault will break his intentions.
*/
#define __user_ok(addr, size) ({ (void)(size); (addr) < STACK_TOP; })
#define __kernel_ok (uaccess_kernel())
#define __access_ok(addr, size) (__user_ok((addr) & get_fs().seg, (size)))
#define access_ok(addr, size) __access_ok((unsigned long)(addr), size)
#include <asm-generic/access_ok.h>
/* Uh, these should become the main single-value transfer routines..
* They automatically use the right size if we just have the right
@@ -47,13 +37,13 @@
* and hide all the ugliness from the user.
*/
#define put_user(x, ptr) ({ \
unsigned long __pu_addr = (unsigned long)(ptr); \
void __user *__pu_addr = (ptr); \
__chk_user_ptr(ptr); \
__put_user_check((__typeof__(*(ptr)))(x), __pu_addr, sizeof(*(ptr))); \
})
#define get_user(x, ptr) ({ \
unsigned long __gu_addr = (unsigned long)(ptr); \
const void __user *__gu_addr = (ptr); \
__chk_user_ptr(ptr); \
__get_user_check((x), __gu_addr, sizeof(*(ptr)), __typeof__(*(ptr))); \
})
@@ -232,7 +222,7 @@ static inline unsigned long __clear_user(void __user *addr, unsigned long size)
static inline unsigned long clear_user(void __user *addr, unsigned long n)
{
if (n && __access_ok((unsigned long) addr, n))
if (n && __access_ok(addr, n))
return __clear_user(addr, n);
else
return n;

View File

@@ -31,7 +31,7 @@
#define get_fs() ((mm_segment_t){(current_thread_info()->current_ds)})
#define uaccess_kernel() (get_fs().seg == KERNEL_DS.seg)
#include <asm-generic/access_ok.h>
#define set_fs(val) \
do { \
@@ -61,16 +61,6 @@ static inline bool __chk_range_not_ok(unsigned long addr, unsigned long size, un
__chk_range_not_ok((unsigned long __force)(addr), size, limit); \
})
static inline int __access_ok(const void __user * addr, unsigned long size)
{
return 1;
}
static inline int access_ok(const void __user * addr, unsigned long size)
{
return 1;
}
void __retl_efault(void);
/* Uh, these should become the main single-value transfer routines..