[tip:x86/asm] x86: Replace assembly access_ok() with a C variant

2013-12-27 Thread tip-bot for Linus Torvalds
Commit-ID:  c5fe5d80680e2949ffe102180f5fc6cefc0d145f
Gitweb: http://git.kernel.org/tip/c5fe5d80680e2949ffe102180f5fc6cefc0d145f
Author: Linus Torvalds 
AuthorDate: Fri, 27 Dec 2013 15:30:58 -0800
Committer:  H. Peter Anvin 
CommitDate: Fri, 27 Dec 2013 16:58:17 -0800

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_=%2bokjfac9xw2awegogtk...@mail.gmail.com
Signed-off-by: H. Peter Anvin 
---
 arch/x86/include/asm/uaccess.h | 28 +---
 1 file changed, 17 insertions(+), 11 deletions(-)

diff --git a/arch/x86/include/asm/uaccess.h b/arch/x86/include/asm/uaccess.h
index 8ec57c0..84ecf1d 100644
--- a/arch/x86/include/asm/uaccess.h
+++ b/arch/x86/include/asm/uaccess.h
@@ -40,22 +40,28 @@
 /*
  * Test whether a block of memory is a valid user space address.
  * Returns 0 if the range is valid, nonzero otherwise.
- *
- * This is equivalent to the following test:
- * (u33)addr + (u33)size > (u33)current->addr_limit.seg (u65 for x86_64)
- *
- * This needs 33-bit (65-bit for x86_64) arithmetic. We have a carry...
  */
+static inline int __chk_range_not_ok(unsigned long addr, unsigned long size, 
unsigned long limit)
+{
+   /*
+* If we have used "sizeof()" for the size,
+* we know it won't overflow the limit (but
+* it might overflow the 'addr', so it's
+* important to subtract the size from the
+* limit, not add it to the address).
+*/
+   if (__builtin_constant_p(size))
+   return addr > limit - size;
+
+   /* Arbitrary sizes? Be careful about overflow */
+   addr += size;
+   return (addr < size) || (addr > limit);
+}
 
 #define __range_not_ok(addr, size, limit)  \
 ({ \
-   unsigned long flag, roksum; \
__chk_user_ptr(addr);   \
-   asm("add %3,%1 ; sbb %0,%0 ; cmp %1,%4 ; sbb $0,%0" \
-   : "=" (flag), "=r" (roksum)   \
-   : "1" (addr), "g" ((long)(size)),   \
- "rm" (limit));\
-   flag;   \
+   __chk_range_not_ok((unsigned long __force)(addr), size, limit); \
 })
 
 /**
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/


[tip:x86/asm] x86: Replace assembly access_ok() with a C variant

2013-12-27 Thread tip-bot for Linus Torvalds
Commit-ID:  c5fe5d80680e2949ffe102180f5fc6cefc0d145f
Gitweb: http://git.kernel.org/tip/c5fe5d80680e2949ffe102180f5fc6cefc0d145f
Author: Linus Torvalds torva...@linux-foundation.org
AuthorDate: Fri, 27 Dec 2013 15:30:58 -0800
Committer:  H. Peter Anvin h...@zytor.com
CommitDate: Fri, 27 Dec 2013 16:58:17 -0800

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_=%2bokjfac9xw2awegogtk...@mail.gmail.com
Signed-off-by: H. Peter Anvin h...@zytor.com
---
 arch/x86/include/asm/uaccess.h | 28 +---
 1 file changed, 17 insertions(+), 11 deletions(-)

diff --git a/arch/x86/include/asm/uaccess.h b/arch/x86/include/asm/uaccess.h
index 8ec57c0..84ecf1d 100644
--- a/arch/x86/include/asm/uaccess.h
+++ b/arch/x86/include/asm/uaccess.h
@@ -40,22 +40,28 @@
 /*
  * Test whether a block of memory is a valid user space address.
  * Returns 0 if the range is valid, nonzero otherwise.
- *
- * This is equivalent to the following test:
- * (u33)addr + (u33)size  (u33)current-addr_limit.seg (u65 for x86_64)
- *
- * This needs 33-bit (65-bit for x86_64) arithmetic. We have a carry...
  */
+static inline int __chk_range_not_ok(unsigned long addr, unsigned long size, 
unsigned long limit)
+{
+   /*
+* If we have used sizeof() for the size,
+* we know it won't overflow the limit (but
+* it might overflow the 'addr', so it's
+* important to subtract the size from the
+* limit, not add it to the address).
+*/
+   if (__builtin_constant_p(size))
+   return addr  limit - size;
+
+   /* Arbitrary sizes? Be careful about overflow */
+   addr += size;
+   return (addr  size) || (addr  limit);
+}
 
 #define __range_not_ok(addr, size, limit)  \
 ({ \
-   unsigned long flag, roksum; \
__chk_user_ptr(addr);   \
-   asm(add %3,%1 ; sbb %0,%0 ; cmp %1,%4 ; sbb $0,%0 \
-   : =r (flag), =r (roksum)   \
-   : 1 (addr), g ((long)(size)),   \
- rm (limit));\
-   flag;   \
+   __chk_range_not_ok((unsigned long __force)(addr), size, limit); \
 })
 
 /**
--
To unsubscribe from this list: send the line unsubscribe linux-kernel in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/