aboutsummaryrefslogtreecommitdiffstats
path: root/arch/x86/include/asm/uaccess.h
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2013-12-27 18:30:58 -0500
committerH. Peter Anvin <hpa@zytor.com>2013-12-27 19:58:17 -0500
commitc5fe5d80680e2949ffe102180f5fc6cefc0d145f (patch)
treeca8b287deb7c2fc0167e31dd08de2f5397a33728 /arch/x86/include/asm/uaccess.h
parent661c80192d21269c7fc566f1d547510b0c867677 (diff)
x86: Replace assembly access_ok() with a C variant
It turns out that the assembly variant doesn't actually produce that good code, presumably partly because it creates a long dependency chain with no scheduling, and partly because we cannot get a flags result out of gcc (which could be fixed with asm goto, but it turns out not to be worth it.) The C code allows gcc to schedule and generate multiple (easily predictable) branches, and as a side benefit we can really optimize the case where the size is constant. Link: http://lkml.kernel.org/r/CA%2B55aFzPBdbfKovMT8Edr4SmE2_=%2BOKJFac9XW2awegogTkVTA@mail.gmail.com Signed-off-by: H. Peter Anvin <hpa@zytor.com>
Diffstat (limited to 'arch/x86/include/asm/uaccess.h')
-rw-r--r--arch/x86/include/asm/uaccess.h28
1 files changed, 17 insertions, 11 deletions
diff --git a/arch/x86/include/asm/uaccess.h b/arch/x86/include/asm/uaccess.h
index 8ec57c07b125..84ecf1df2ac6 100644
--- a/arch/x86/include/asm/uaccess.h
+++ b/arch/x86/include/asm/uaccess.h
@@ -40,22 +40,28 @@
40/* 40/*
41 * Test whether a block of memory is a valid user space address. 41 * Test whether a block of memory is a valid user space address.
42 * Returns 0 if the range is valid, nonzero otherwise. 42 * Returns 0 if the range is valid, nonzero otherwise.
43 *
44 * This is equivalent to the following test:
45 * (u33)addr + (u33)size > (u33)current->addr_limit.seg (u65 for x86_64)
46 *
47 * This needs 33-bit (65-bit for x86_64) arithmetic. We have a carry...
48 */ 43 */
44static inline int __chk_range_not_ok(unsigned long addr, unsigned long size, unsigned long limit)
45{
46 /*
47 * If we have used "sizeof()" for the size,
48 * we know it won't overflow the limit (but
49 * it might overflow the 'addr', so it's
50 * important to subtract the size from the
51 * limit, not add it to the address).
52 */
53 if (__builtin_constant_p(size))
54 return addr > limit - size;
55
56 /* Arbitrary sizes? Be careful about overflow */
57 addr += size;
58 return (addr < size) || (addr > limit);
59}
49 60
50#define __range_not_ok(addr, size, limit) \ 61#define __range_not_ok(addr, size, limit) \
51({ \ 62({ \
52 unsigned long flag, roksum; \
53 __chk_user_ptr(addr); \ 63 __chk_user_ptr(addr); \
54 asm("add %3,%1 ; sbb %0,%0 ; cmp %1,%4 ; sbb $0,%0" \ 64 __chk_range_not_ok((unsigned long __force)(addr), size, limit); \
55 : "=&r" (flag), "=r" (roksum) \
56 : "1" (addr), "g" ((long)(size)), \
57 "rm" (limit)); \
58 flag; \
59}) 65})
60 66
61/** 67/**