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
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;
}
......
#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)
#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))
......
......@@ -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
......
......@@ -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) \
......
......@@ -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);
/**
......
......@@ -14,6 +14,7 @@
#include <asm/asi.h>
#include <asm/system.h>
#include <asm/spitfire.h>
#include <asm-generic/uaccess.h>
#endif
#ifndef __ASSEMBLY__
......
......@@ -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; \
......
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