diff options
| author | Arun Sharma <arun.sharma@intel.com> | 2004-08-26 20:43:14 -0700 |
|---|---|---|
| committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2004-08-26 20:43:14 -0700 |
| commit | ce18373907856aedaa6251ff3cdd1340ee4af29a (patch) | |
| tree | f6067623ca7a61bc55b122b90c90d147732e5615 | |
| parent | fec18575fbb0faf522afbf5b5288962a79ed542f (diff) | |
[PATCH] Fix copying of unaligned data across user/kernel boundary
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>
| -rw-r--r-- | fs/compat.c | 14 | ||||
| -rw-r--r-- | include/asm-generic/uaccess.h | 26 | ||||
| -rw-r--r-- | include/asm-ia64/uaccess.h | 36 | ||||
| -rw-r--r-- | include/asm-mips/uaccess.h | 1 | ||||
| -rw-r--r-- | include/asm-ppc64/uaccess.h | 3 | ||||
| -rw-r--r-- | include/asm-s390/uaccess.h | 3 | ||||
| -rw-r--r-- | include/asm-sparc64/uaccess.h | 1 | ||||
| -rw-r--r-- | include/asm-x86_64/uaccess.h | 3 |
8 files changed, 77 insertions, 10 deletions
diff --git a/fs/compat.c b/fs/compat.c index 919a95f37c91..3c2a458804ef 100644 --- a/fs/compat.c +++ b/fs/compat.c @@ -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 index 000000000000..4d62a1cf543d --- /dev/null +++ b/include/asm-generic/uaccess.h @@ -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 */ diff --git a/include/asm-ia64/uaccess.h b/include/asm-ia64/uaccess.h index b52fd3244be1..2c7743e363b8 100644 --- a/include/asm-ia64/uaccess.h +++ b/include/asm-ia64/uaccess.h @@ -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)) diff --git a/include/asm-mips/uaccess.h b/include/asm-mips/uaccess.h index 759d072b6c2d..adbb24bd920f 100644 --- a/include/asm-mips/uaccess.h +++ b/include/asm-mips/uaccess.h @@ -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 diff --git a/include/asm-ppc64/uaccess.h b/include/asm-ppc64/uaccess.h index 16dbcb999335..58eec5324170 100644 --- a/include/asm-ppc64/uaccess.h +++ b/include/asm-ppc64/uaccess.h @@ -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) \ diff --git a/include/asm-s390/uaccess.h b/include/asm-s390/uaccess.h index 0b654b318e33..5eda2c334dc7 100644 --- a/include/asm-s390/uaccess.h +++ b/include/asm-s390/uaccess.h @@ -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); /** diff --git a/include/asm-sparc64/uaccess.h b/include/asm-sparc64/uaccess.h index bc5d79fbddd3..1aff19cd0ecf 100644 --- a/include/asm-sparc64/uaccess.h +++ b/include/asm-sparc64/uaccess.h @@ -14,6 +14,7 @@ #include <asm/asi.h> #include <asm/system.h> #include <asm/spitfire.h> +#include <asm-generic/uaccess.h> #endif #ifndef __ASSEMBLY__ diff --git a/include/asm-x86_64/uaccess.h b/include/asm-x86_64/uaccess.h index 8d0598d4e11d..a7b9d33053c4 100644 --- a/include/asm-x86_64/uaccess.h +++ b/include/asm-x86_64/uaccess.h @@ -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; \ |
