From: Arun Sharma 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 for his help. Signed-off-by: Gordon Jin Signed-off-by: Arun Sharma Signed-off-by: Andrew Morton --- 25-akpm/fs/compat.c | 14 +++---------- 25-akpm/include/asm-generic/uaccess.h | 25 +++++++++++++++++++++++ 25-akpm/include/asm-ia64/uaccess.h | 36 ++++++++++++++++++++++++++++++++++ 25-akpm/include/asm-mips/uaccess.h | 1 25-akpm/include/asm-ppc64/uaccess.h | 3 ++ 25-akpm/include/asm-s390/uaccess.h | 3 ++ 25-akpm/include/asm-sparc64/uaccess.h | 1 25-akpm/include/asm-x86_64/uaccess.h | 3 ++ 8 files changed, 76 insertions(+), 10 deletions(-) diff -puN fs/compat.c~copying-unaligned-data-across-user-kernel-boundary fs/compat.c --- 25/fs/compat.c~copying-unaligned-data-across-user-kernel-boundary 2004-08-24 17:32:48.778498656 -0700 +++ 25-akpm/fs/compat.c 2004-08-24 17:32:48.798495616 -0700 @@ -981,19 +981,14 @@ static int compat_filldir64(void * __buf 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(un 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 -puN /dev/null include/asm-generic/uaccess.h --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25-akpm/include/asm-generic/uaccess.h 2004-08-24 17:32:48.798495616 -0700 @@ -0,0 +1,25 @@ +#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 unknown to be aligned. + */ +#define __get_user_unaligned(x, ptr) \ +({ \ + __typeof__ (*(ptr)) __x = (x); \ + __copy_from_user(&__x, (ptr), sizeof(*(ptr))) ? -EFAULT : 0; \ +}) + + +/* + * This macro should be used instead of __put_user() when accessing + * values at locations that are unknown 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 -puN include/asm-ia64/uaccess.h~copying-unaligned-data-across-user-kernel-boundary include/asm-ia64/uaccess.h --- 25/include/asm-ia64/uaccess.h~copying-unaligned-data-across-user-kernel-boundary 2004-08-24 17:32:48.780498352 -0700 +++ 25-akpm/include/asm-ia64/uaccess.h 2004-08-24 17:32:48.800495312 -0700 @@ -91,6 +91,42 @@ verify_area (int type, const void *addr, #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 -puN include/asm-mips/uaccess.h~copying-unaligned-data-across-user-kernel-boundary include/asm-mips/uaccess.h --- 25/include/asm-mips/uaccess.h~copying-unaligned-data-across-user-kernel-boundary 2004-08-24 17:32:48.782498048 -0700 +++ 25-akpm/include/asm-mips/uaccess.h 2004-08-24 17:32:48.801495160 -0700 @@ -13,6 +13,7 @@ #include #include #include +#include /* * The fs value determines whether argument validity checking should be diff -puN include/asm-ppc64/uaccess.h~copying-unaligned-data-across-user-kernel-boundary include/asm-ppc64/uaccess.h --- 25/include/asm-ppc64/uaccess.h~copying-unaligned-data-across-user-kernel-boundary 2004-08-24 17:32:48.784497744 -0700 +++ 25-akpm/include/asm-ppc64/uaccess.h 2004-08-24 17:32:48.802495008 -0700 @@ -111,6 +111,9 @@ extern unsigned long search_exception_ta #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 -puN include/asm-s390/uaccess.h~copying-unaligned-data-across-user-kernel-boundary include/asm-s390/uaccess.h --- 25/include/asm-s390/uaccess.h~copying-unaligned-data-across-user-kernel-boundary 2004-08-24 17:32:48.786497440 -0700 +++ 25-akpm/include/asm-s390/uaccess.h 2004-08-24 17:32:48.803494856 -0700 @@ -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 -puN include/asm-sparc64/uaccess.h~copying-unaligned-data-across-user-kernel-boundary include/asm-sparc64/uaccess.h --- 25/include/asm-sparc64/uaccess.h~copying-unaligned-data-across-user-kernel-boundary 2004-08-24 17:32:48.789496984 -0700 +++ 25-akpm/include/asm-sparc64/uaccess.h 2004-08-24 17:32:48.804494704 -0700 @@ -14,6 +14,7 @@ #include #include #include +#include #endif #ifndef __ASSEMBLY__ diff -puN include/asm-x86_64/uaccess.h~copying-unaligned-data-across-user-kernel-boundary include/asm-x86_64/uaccess.h --- 25/include/asm-x86_64/uaccess.h~copying-unaligned-data-across-user-kernel-boundary 2004-08-24 17:32:48.791496680 -0700 +++ 25-akpm/include/asm-x86_64/uaccess.h 2004-08-24 17:32:48.804494704 -0700 @@ -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; \ _