diff options
author | David S. Miller <davem@davemloft.net> | 2012-05-23 22:56:06 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2012-05-24 16:04:24 -0400 |
commit | 35c9646062eba15351c6d64ea0c02176e5cb66ac (patch) | |
tree | 46b29964dfbf21e6757b5ab629c6f748897d34e5 /arch | |
parent | 4efcac3a244de86593a82ca4ed945e839eb4c5af (diff) |
sparc: Increase portability of strncpy_from_user() implementation.
Hide details of maximum user address calculation in a new
asm/uaccess.h interface named user_addr_max().
Provide little-endian implementation in find_zero(), which should work
but can probably be improved.
Abstrace alignment check behind IS_UNALIGNED() macro.
Kill double-semicolon, noticed by David Howells.
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'arch')
-rw-r--r-- | arch/sparc/include/asm/uaccess.h | 3 | ||||
-rw-r--r-- | arch/sparc/lib/usercopy.c | 32 |
2 files changed, 30 insertions, 5 deletions
diff --git a/arch/sparc/include/asm/uaccess.h b/arch/sparc/include/asm/uaccess.h index 42a28cfd941f..20c2acb06332 100644 --- a/arch/sparc/include/asm/uaccess.h +++ b/arch/sparc/include/asm/uaccess.h | |||
@@ -6,6 +6,9 @@ | |||
6 | #include <asm/uaccess_32.h> | 6 | #include <asm/uaccess_32.h> |
7 | #endif | 7 | #endif |
8 | 8 | ||
9 | #define user_addr_max() \ | ||
10 | (segment_eq(get_fs(), USER_DS) ? STACK_TOP : ~0UL) | ||
11 | |||
9 | extern long strncpy_from_user(char *dest, const char __user *src, long count); | 12 | extern long strncpy_from_user(char *dest, const char __user *src, long count); |
10 | 13 | ||
11 | #endif | 14 | #endif |
diff --git a/arch/sparc/lib/usercopy.c b/arch/sparc/lib/usercopy.c index 87f96453403a..f61ed820cb61 100644 --- a/arch/sparc/lib/usercopy.c +++ b/arch/sparc/lib/usercopy.c | |||
@@ -3,6 +3,8 @@ | |||
3 | #include <linux/errno.h> | 3 | #include <linux/errno.h> |
4 | #include <linux/bug.h> | 4 | #include <linux/bug.h> |
5 | 5 | ||
6 | #include <asm/byteorder.h> | ||
7 | |||
6 | void copy_from_user_overflow(void) | 8 | void copy_from_user_overflow(void) |
7 | { | 9 | { |
8 | WARN(1, "Buffer overflow detected!\n"); | 10 | WARN(1, "Buffer overflow detected!\n"); |
@@ -14,6 +16,8 @@ EXPORT_SYMBOL(copy_from_user_overflow); | |||
14 | static inline long find_zero(unsigned long mask) | 16 | static inline long find_zero(unsigned long mask) |
15 | { | 17 | { |
16 | long byte = 0; | 18 | long byte = 0; |
19 | |||
20 | #ifdef __BIG_ENDIAN | ||
17 | #ifdef CONFIG_64BIT | 21 | #ifdef CONFIG_64BIT |
18 | if (mask >> 32) | 22 | if (mask >> 32) |
19 | mask >>= 32; | 23 | mask >>= 32; |
@@ -25,8 +29,28 @@ static inline long find_zero(unsigned long mask) | |||
25 | else | 29 | else |
26 | byte += 2; | 30 | byte += 2; |
27 | return (mask >> 8) ? byte : byte + 1; | 31 | return (mask >> 8) ? byte : byte + 1; |
32 | #else | ||
33 | #ifdef CONFIG_64BIT | ||
34 | if (!((unsigned int) mask)) { | ||
35 | mask >>= 32; | ||
36 | byte = 4; | ||
37 | } | ||
38 | #endif | ||
39 | if (!(mask & 0xffff)) { | ||
40 | mask >>= 16; | ||
41 | byte += 2; | ||
42 | } | ||
43 | return (mask & 0xff) ? byte : byte + 1; | ||
44 | #endif | ||
28 | } | 45 | } |
29 | 46 | ||
47 | #ifdef CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS | ||
48 | #define IS_UNALIGNED(src, dst) 0 | ||
49 | #else | ||
50 | #define IS_UNALIGNED(src, dst) \ | ||
51 | (((long) dst | (long) src) & (sizeof(long) - 1)) | ||
52 | #endif | ||
53 | |||
30 | /* | 54 | /* |
31 | * Do a strncpy, return length of string without final '\0'. | 55 | * Do a strncpy, return length of string without final '\0'. |
32 | * 'count' is the user-supplied count (return 'count' if we | 56 | * 'count' is the user-supplied count (return 'count' if we |
@@ -46,7 +70,7 @@ static inline long do_strncpy_from_user(char *dst, const char __user *src, long | |||
46 | if (max > count) | 70 | if (max > count) |
47 | max = count; | 71 | max = count; |
48 | 72 | ||
49 | if (((long) dst | (long) src) & (sizeof(long) - 1)) | 73 | if (IS_UNALIGNED(src, dst)) |
50 | goto byte_at_a_time; | 74 | goto byte_at_a_time; |
51 | 75 | ||
52 | while (max >= sizeof(unsigned long)) { | 76 | while (max >= sizeof(unsigned long)) { |
@@ -59,7 +83,7 @@ static inline long do_strncpy_from_user(char *dst, const char __user *src, long | |||
59 | v = (c + high_bits) & ~rhs; | 83 | v = (c + high_bits) & ~rhs; |
60 | *(unsigned long *)(dst+res) = c; | 84 | *(unsigned long *)(dst+res) = c; |
61 | if (v) { | 85 | if (v) { |
62 | v = (c & low_bits) + low_bits;; | 86 | v = (c & low_bits) + low_bits; |
63 | v = ~(v | rhs); | 87 | v = ~(v | rhs); |
64 | return res + find_zero(v); | 88 | return res + find_zero(v); |
65 | } | 89 | } |
@@ -119,9 +143,7 @@ long strncpy_from_user(char *dst, const char __user *src, long count) | |||
119 | if (unlikely(count <= 0)) | 143 | if (unlikely(count <= 0)) |
120 | return 0; | 144 | return 0; |
121 | 145 | ||
122 | max_addr = ~0UL; | 146 | max_addr = user_addr_max(); |
123 | if (likely(segment_eq(get_fs(), USER_DS))) | ||
124 | max_addr = STACK_TOP; | ||
125 | src_addr = (unsigned long)src; | 147 | src_addr = (unsigned long)src; |
126 | if (likely(src_addr < max_addr)) { | 148 | if (likely(src_addr < max_addr)) { |
127 | unsigned long max = max_addr - src_addr; | 149 | unsigned long max = max_addr - src_addr; |