summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorArun Sharma <arun.sharma@intel.com>2004-08-26 20:43:14 -0700
committerLinus Torvalds <torvalds@ppc970.osdl.org>2004-08-26 20:43:14 -0700
commitce18373907856aedaa6251ff3cdd1340ee4af29a (patch)
treef6067623ca7a61bc55b122b90c90d147732e5615
parentfec18575fbb0faf522afbf5b5288962a79ed542f (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.c14
-rw-r--r--include/asm-generic/uaccess.h26
-rw-r--r--include/asm-ia64/uaccess.h36
-rw-r--r--include/asm-mips/uaccess.h1
-rw-r--r--include/asm-ppc64/uaccess.h3
-rw-r--r--include/asm-s390/uaccess.h3
-rw-r--r--include/asm-sparc64/uaccess.h1
-rw-r--r--include/asm-x86_64/uaccess.h3
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; \