]> git.hungrycats.org Git - linux/commitdiff
[PATCH] Fix copying of unaligned data across user/kernel boundary
authorArun Sharma <arun.sharma@intel.com>
Fri, 27 Aug 2004 03:43:14 +0000 (20:43 -0700)
committerLinus Torvalds <torvalds@ppc970.osdl.org>
Fri, 27 Aug 2004 03:43:14 +0000 (20:43 -0700)
32 bit compatibility code sometimes needs to copy unaligned data across
kernel/user boundary and currently there is no architecture independent API
to do it.

(1) Introduce new APIs __{get,put}_user_unaligned. These APIs are
    necessary because the optimal way to copy unaligned data across
    kernel/user boundary is different on different architectures.
    Some architectures don't even care about alignment.
    On some __put_user is faster than __copy_to_user for small sizes.
(2) Optimize __{get,put}_user_unaligned for ia64, x86-64, s390, ppc64.
(3) Fix compat_filldir64() which is broken on big-endian machines

Thanks to Arnd Bergmann <arnd@arndb.de> for his help.

Signed-off-by: Gordon Jin <gordon.jin@intel.com>
Signed-off-by: Arun Sharma <arun.sharma@intel.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
fs/compat.c
include/asm-generic/uaccess.h [new file with mode: 0644]
include/asm-ia64/uaccess.h
include/asm-mips/uaccess.h
include/asm-ppc64/uaccess.h
include/asm-s390/uaccess.h
include/asm-sparc64/uaccess.h
include/asm-x86_64/uaccess.h

index 919a95f37c912e1d08e95e09f2729eec4a159366..3c2a458804ef4363a4a12a4a58b510bbd6116e58 100644 (file)
@@ -981,19 +981,14 @@ static int compat_filldir64(void * __buf, const char * name, int namlen, loff_t
        dirent = buf->previous;
 
        if (dirent) {
-               if (__put_user(offset, (u32 __user *)&dirent->d_off))
-                       goto efault;
-               if (__put_user(offset >> 32,
-                               ((u32 __user *)&dirent->d_off) + 1))
+               if (__put_user_unaligned(offset, &dirent->d_off))
                        goto efault;
        }
        dirent = buf->current_dir;
-       if ((__put_user(ino, (u32 __user *)&dirent->d_ino))
-        || (__put_user(ino >> 32, ((u32 __user *)&dirent->d_ino) + 1)))
+       if (__put_user_unaligned(ino, &dirent->d_ino))
                goto efault;
        off = 0;
-       if ((__put_user(off, (u32 __user *)&dirent->d_off))
-        || (__put_user(off >> 32, ((u32 __user *)&dirent->d_off) + 1)))
+       if (__put_user_unaligned(off, &dirent->d_off))
                goto efault;
        if (__put_user(reclen, &dirent->d_reclen))
                goto efault;
@@ -1042,8 +1037,7 @@ asmlinkage long compat_sys_getdents64(unsigned int fd,
        lastdirent = buf.previous;
        if (lastdirent) {
                typeof(lastdirent->d_off) d_off = file->f_pos;
-               __put_user(d_off, (u32 __user *)&lastdirent->d_off);
-               __put_user(d_off >> 32, ((u32 __user *)&lastdirent->d_off) + 1);
+               __put_user_unaligned(d_off, &lastdirent->d_off);
                error = count - buf.count;
        }
 
diff --git a/include/asm-generic/uaccess.h b/include/asm-generic/uaccess.h
new file mode 100644 (file)
index 0000000..4d62a1c
--- /dev/null
@@ -0,0 +1,26 @@
+#ifndef _ASM_GENERIC_UACCESS_H_
+#define _ASM_GENERIC_UACCESS_H_
+
+/*
+ * This macro should be used instead of __get_user() when accessing
+ * values at locations that are not known to be aligned.
+ */
+#define __get_user_unaligned(x, ptr)                                   \
+({                                                                     \
+       __typeof__ (*(ptr)) __x;                                        \
+       __copy_from_user(&__x, (ptr), sizeof(*(ptr))) ? -EFAULT : 0;    \
+       (x) = _x;                                                       \
+})
+
+
+/*
+ * This macro should be used instead of __put_user() when accessing
+ * values at locations that are not known to be aligned.
+ */
+#define __put_user_unaligned(x, ptr)                                   \
+({                                                                     \
+       __typeof__ (*(ptr)) __x = (x);                                  \
+       __copy_to_user((ptr), &__x, sizeof(*(ptr))) ? -EFAULT : 0;      \
+})
+
+#endif /* _ASM_GENERIC_UACCESS_H */
index b52fd3244be1c48dbe2ab1d6a630984d4b38c668..2c7743e363b81ba3516dc9ba5d1537c13a39e72f 100644 (file)
@@ -91,6 +91,42 @@ verify_area (int type, const void *addr, unsigned long size)
 #define __put_user(x, ptr)     __put_user_nocheck((__typeof__(*(ptr))) (x), (ptr), sizeof(*(ptr)))
 #define __get_user(x, ptr)     __get_user_nocheck((x), (ptr), sizeof(*(ptr)))
 
+extern long __put_user_unaligned_unknown (void);
+
+#define __put_user_unaligned(x, ptr)                                                           \
+({                                                                                             \
+       long __ret;                                                                             \
+       switch (sizeof(*(ptr))) {                                                               \
+               case 1: __ret = __put_user((x), (ptr)); break;                                  \
+               case 2: __ret = (__put_user((x), (u8 __user *)(ptr)))                           \
+                       | (__put_user((x) >> 8, ((u8 __user *)(ptr) + 1))); break;              \
+               case 4: __ret = (__put_user((x), (u16 __user *)(ptr)))                          \
+                       | (__put_user((x) >> 16, ((u16 __user *)(ptr) + 1))); break;            \
+               case 8: __ret = (__put_user((x), (u32 __user *)(ptr)))                          \
+                       | (__put_user((x) >> 32, ((u32 __user *)(ptr) + 1))); break;            \
+               default: __ret = __put_user_unaligned_unknown();                                \
+       }                                                                                       \
+       __ret;                                                                                  \
+})
+
+extern long __get_user_unaligned_unknown (void);
+
+#define __get_user_unaligned(x, ptr)                                                           \
+({                                                                                             \
+       long __ret;                                                                             \
+       switch (sizeof(*(ptr))) {                                                               \
+               case 1: __ret = __get_user((x), (ptr)); break;                                  \
+               case 2: __ret = (__get_user((x), (u8 __user *)(ptr)))                           \
+                       | (__get_user((x) >> 8, ((u8 __user *)(ptr) + 1))); break;              \
+               case 4: __ret = (__get_user((x), (u16 __user *)(ptr)))                          \
+                       | (__get_user((x) >> 16, ((u16 __user *)(ptr) + 1))); break;            \
+               case 8: __ret = (__get_user((x), (u32 __user *)(ptr)))                          \
+                       | (__get_user((x) >> 32, ((u32 __user *)(ptr) + 1))); break;            \
+               default: __ret = __get_user_unaligned_unknown();                                \
+       }                                                                                       \
+       __ret;                                                                                  \
+})
+
 #ifdef ASM_SUPPORTED
   struct __large_struct { unsigned long buf[100]; };
 # define __m(x) (*(struct __large_struct *)(x))
index 759d072b6c2dcf54ed00c00d363ea2cad51c0c93..adbb24bd920f50deabe28c74a53c9c64dc1497c8 100644 (file)
@@ -13,6 +13,7 @@
 #include <linux/compiler.h>
 #include <linux/errno.h>
 #include <linux/thread_info.h>
+#include <asm-generic/uaccess.h>
 
 /*
  * The fs value determines whether argument validity checking should be
index 16dbcb9993353a9c6112751c4782d25c69448930..58eec5324170345fce155b34bdddfbeac22e430e 100644 (file)
@@ -111,6 +111,9 @@ extern unsigned long search_exception_table(unsigned long);
 #define __put_user(x,ptr) \
   __put_user_nocheck((__typeof__(*(ptr)))(x),(ptr),sizeof(*(ptr)))
 
+#define __get_user_unaligned __get_user
+#define __put_user_unaligned __put_user
+
 extern long __put_user_bad(void);
 
 #define __put_user_nocheck(x,ptr,size)                         \
index 0b654b318e335424a6930261997e055f092792b3..5eda2c334dc7b96631a7e559871d52b00612335d 100644 (file)
@@ -250,6 +250,9 @@ extern int __put_user_bad(void);
 
 extern int __get_user_bad(void);
 
+#define __put_user_unaligned __put_user
+#define __get_user_unaligned __get_user
+
 extern long __copy_to_user_asm(const void *from, long n, void __user *to);
 
 /**
index bc5d79fbddd3d9640c2c2217f583896ab3643eeb..1aff19cd0ecf777cca4d32af611fd93c79c36ce9 100644 (file)
@@ -14,6 +14,7 @@
 #include <asm/asi.h>
 #include <asm/system.h>
 #include <asm/spitfire.h>
+#include <asm-generic/uaccess.h>
 #endif
 
 #ifndef __ASSEMBLY__
index 8d0598d4e11da6b8ea06c8f2318bcdf4da5e7df9..a7b9d33053c4d6d13297df96427929160a496a78 100644 (file)
@@ -137,6 +137,9 @@ extern void __put_user_bad(void);
 #define __put_user(x,ptr) \
   __put_user_nocheck((__typeof__(*(ptr)))(x),(ptr),sizeof(*(ptr)))
 
+#define __get_user_unaligned __get_user
+#define __put_user_unaligned __put_user
+
 #define __put_user_nocheck(x,ptr,size)                 \
 ({                                                     \
        int __pu_err;                                   \