sparc: Use __builtin_object_size() to validate the buffer size for copy_from_user()
authorDavid S. Miller <davem@davemloft.net>
Fri, 11 Dec 2009 07:05:23 +0000 (23:05 -0800)
committerDavid S. Miller <davem@davemloft.net>
Fri, 11 Dec 2009 07:05:23 +0000 (23:05 -0800)
This mirrors x86 commit 9f0cf4adb6aa0bfccf675c938124e68f7f06349d
(x86: Use __builtin_object_size() to validate the buffer size for copy_from_user())

Signed-off-by: David S. Miller <davem@davemloft.net>
arch/sparc/Kconfig.debug
arch/sparc/include/asm/uaccess_32.h
arch/sparc/include/asm/uaccess_64.h
arch/sparc/lib/Makefile
arch/sparc/lib/usercopy.c [new file with mode: 0644]

index 90d5fe223a7458953e866aa376ebd8e5591f244b..9d3c889718ac60e954d2439d74847a3443ed825c 100644 (file)
@@ -33,4 +33,18 @@ config FRAME_POINTER
        depends on MCOUNT
        default y
 
+config DEBUG_STRICT_USER_COPY_CHECKS
+       bool "Strict copy size checks"
+       depends on DEBUG_KERNEL && !TRACE_BRANCH_PROFILING
+       ---help---
+         Enabling this option turns a certain set of sanity checks for user
+         copy operations into compile time failures.
+
+         The copy_from_user() etc checks are there to help test if there
+         are sufficient security checks on the length argument of
+         the copy operation, by having gcc prove that the argument is
+         within bounds.
+
+         If unsure, or if you run an older (pre 4.4) gcc, say N.
+
 endmenu
index 8303ac48103426542f6681d826c995f8b8375385..489d2ba92bcb504e3d6243e36cef99bddf08e4c3 100644 (file)
@@ -260,8 +260,23 @@ static inline unsigned long __copy_to_user(void __user *to, const void *from, un
        return __copy_user(to, (__force void __user *) from, n);
 }
 
+extern void copy_from_user_overflow(void)
+#ifdef CONFIG_DEBUG_STRICT_USER_COPY_CHECKS
+       __compiletime_error("copy_from_user() buffer size is not provably correct")
+#else
+       __compiletime_warning("copy_from_user() buffer size is not provably correct")
+#endif
+;
+
 static inline unsigned long copy_from_user(void *to, const void __user *from, unsigned long n)
 {
+       int sz = __compiletime_object_size(to);
+
+       if (unlikely(sz != -1 && sz < n)) {
+               copy_from_user_overflow();
+               return -EFAULT;
+       }
+
        if (n && __access_ok((unsigned long) from, n))
                return __copy_user((__force void __user *) to, from, n);
        else
index 9ea271e19c70a9be95c4d40f3da34faea0b6c4a9..dbc141660994915c23c059b844772ae855681bdc 100644 (file)
@@ -6,6 +6,7 @@
  */
 
 #ifdef __KERNEL__
+#include <linux/errno.h>
 #include <linux/compiler.h>
 #include <linux/string.h>
 #include <linux/thread_info.h>
@@ -204,6 +205,14 @@ __asm__ __volatile__(                                                      \
 
 extern int __get_user_bad(void);
 
+extern void copy_from_user_overflow(void)
+#ifdef CONFIG_DEBUG_STRICT_USER_COPY_CHECKS
+       __compiletime_error("copy_from_user() buffer size is not provably correct")
+#else
+       __compiletime_warning("copy_from_user() buffer size is not provably correct")
+#endif
+;
+
 extern unsigned long __must_check ___copy_from_user(void *to,
                                                    const void __user *from,
                                                    unsigned long size);
@@ -212,10 +221,16 @@ extern unsigned long copy_from_user_fixup(void *to, const void __user *from,
 static inline unsigned long __must_check
 copy_from_user(void *to, const void __user *from, unsigned long size)
 {
-       unsigned long ret = ___copy_from_user(to, from, size);
-
-       if (unlikely(ret))
-               ret = copy_from_user_fixup(to, from, size);
+       unsigned long ret = (unsigned long) -EFAULT;
+       int sz = __compiletime_object_size(to);
+
+       if (likely(sz == -1 || sz >= size)) {
+               ret = ___copy_from_user(to, from, size);
+               if (unlikely(ret))
+                       ret = copy_from_user_fixup(to, from, size);
+       } else {
+               copy_from_user_overflow();
+       }
        return ret;
 }
 #define __copy_from_user copy_from_user
index e75faf0e59aeadec986fb4ad1e22d77e72f040e7..c4b5e03af11557efa6ae42aa11f9ce0d74dfa42a 100644 (file)
@@ -44,3 +44,4 @@ obj-y                 += iomap.o
 obj-$(CONFIG_SPARC32) += atomic32.o
 obj-y                 += ksyms.o
 obj-$(CONFIG_SPARC64) += PeeCeeI.o
+obj-y                 += usercopy.o
diff --git a/arch/sparc/lib/usercopy.c b/arch/sparc/lib/usercopy.c
new file mode 100644 (file)
index 0000000..14b363f
--- /dev/null
@@ -0,0 +1,8 @@
+#include <linux/module.h>
+#include <linux/bug.h>
+
+void copy_from_user_overflow(void)
+{
+       WARN(1, "Buffer overflow detected!\n");
+}
+EXPORT_SYMBOL(copy_from_user_overflow);