Commit ce183739 authored by Arun Sharma's avatar Arun Sharma Committed by Linus Torvalds

[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: default avatarGordon Jin <gordon.jin@intel.com>
Signed-off-by: default avatarArun Sharma <arun.sharma@intel.com>
Signed-off-by: default avatarAndrew Morton <akpm@osdl.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@osdl.org>
parent fec18575
...@@ -981,19 +981,14 @@ static int compat_filldir64(void * __buf, const char * name, int namlen, loff_t ...@@ -981,19 +981,14 @@ static int compat_filldir64(void * __buf, const char * name, int namlen, loff_t
dirent = buf->previous; dirent = buf->previous;
if (dirent) { if (dirent) {
if (__put_user(offset, (u32 __user *)&dirent->d_off)) if (__put_user_unaligned(offset, &dirent->d_off))
goto efault;
if (__put_user(offset >> 32,
((u32 __user *)&dirent->d_off) + 1))
goto efault; goto efault;
} }
dirent = buf->current_dir; dirent = buf->current_dir;
if ((__put_user(ino, (u32 __user *)&dirent->d_ino)) if (__put_user_unaligned(ino, &dirent->d_ino))
|| (__put_user(ino >> 32, ((u32 __user *)&dirent->d_ino) + 1)))
goto efault; goto efault;
off = 0; off = 0;
if ((__put_user(off, (u32 __user *)&dirent->d_off)) if (__put_user_unaligned(off, &dirent->d_off))
|| (__put_user(off >> 32, ((u32 __user *)&dirent->d_off) + 1)))
goto efault; goto efault;
if (__put_user(reclen, &dirent->d_reclen)) if (__put_user(reclen, &dirent->d_reclen))
goto efault; goto efault;
...@@ -1042,8 +1037,7 @@ asmlinkage long compat_sys_getdents64(unsigned int fd, ...@@ -1042,8 +1037,7 @@ asmlinkage long compat_sys_getdents64(unsigned int fd,
lastdirent = buf.previous; lastdirent = buf.previous;
if (lastdirent) { if (lastdirent) {
typeof(lastdirent->d_off) d_off = file->f_pos; typeof(lastdirent->d_off) d_off = file->f_pos;
__put_user(d_off, (u32 __user *)&lastdirent->d_off); __put_user_unaligned(d_off, &lastdirent->d_off);
__put_user(d_off >> 32, ((u32 __user *)&lastdirent->d_off) + 1);
error = count - buf.count; error = count - buf.count;
} }
......
#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 */
...@@ -91,6 +91,42 @@ verify_area (int type, const void *addr, unsigned long size) ...@@ -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 __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))) #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 #ifdef ASM_SUPPORTED
struct __large_struct { unsigned long buf[100]; }; struct __large_struct { unsigned long buf[100]; };
# define __m(x) (*(struct __large_struct *)(x)) # define __m(x) (*(struct __large_struct *)(x))
......
...@@ -13,6 +13,7 @@ ...@@ -13,6 +13,7 @@
#include <linux/compiler.h> #include <linux/compiler.h>
#include <linux/errno.h> #include <linux/errno.h>
#include <linux/thread_info.h> #include <linux/thread_info.h>
#include <asm-generic/uaccess.h>
/* /*
* The fs value determines whether argument validity checking should be * The fs value determines whether argument validity checking should be
......
...@@ -111,6 +111,9 @@ extern unsigned long search_exception_table(unsigned long); ...@@ -111,6 +111,9 @@ extern unsigned long search_exception_table(unsigned long);
#define __put_user(x,ptr) \ #define __put_user(x,ptr) \
__put_user_nocheck((__typeof__(*(ptr)))(x),(ptr),sizeof(*(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); extern long __put_user_bad(void);
#define __put_user_nocheck(x,ptr,size) \ #define __put_user_nocheck(x,ptr,size) \
......
...@@ -250,6 +250,9 @@ extern int __put_user_bad(void); ...@@ -250,6 +250,9 @@ extern int __put_user_bad(void);
extern int __get_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); extern long __copy_to_user_asm(const void *from, long n, void __user *to);
/** /**
......
...@@ -14,6 +14,7 @@ ...@@ -14,6 +14,7 @@
#include <asm/asi.h> #include <asm/asi.h>
#include <asm/system.h> #include <asm/system.h>
#include <asm/spitfire.h> #include <asm/spitfire.h>
#include <asm-generic/uaccess.h>
#endif #endif
#ifndef __ASSEMBLY__ #ifndef __ASSEMBLY__
......
...@@ -137,6 +137,9 @@ extern void __put_user_bad(void); ...@@ -137,6 +137,9 @@ extern void __put_user_bad(void);
#define __put_user(x,ptr) \ #define __put_user(x,ptr) \
__put_user_nocheck((__typeof__(*(ptr)))(x),(ptr),sizeof(*(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) \ #define __put_user_nocheck(x,ptr,size) \
({ \ ({ \
int __pu_err; \ int __pu_err; \
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment