aboutsummaryrefslogtreecommitdiffstats
path: root/lib
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2012-05-26 19:57:16 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2012-05-26 19:57:16 -0400
commit1e2aec873ad6d16538512dbb96853caa1fa076af (patch)
treed792b19ac47be44debd24610ae27f1330fa490e4 /lib
parentae32adc1e06d096399f195eeda12d443d53539c4 (diff)
parent2c66f623631709aa5f2e4c14c7e089682e7394a3 (diff)
Merge branch 'generic-string-functions'
This makes <asm/word-at-a-time.h> actually live up to its promise of allowing architectures to help tune the string functions that do their work a word at a time. David had already taken the x86 strncpy_from_user() function, modified it to work on sparc, and then done the extra work to make it generically useful. This then expands on that work by making x86 use that generic version, completing the circle. But more importantly, it fixes up the word-at-a-time interfaces so that it's now easy to also support things like strnlen_user(), and pretty much most random string functions. David reports that it all works fine on sparc, and Jonas Bonn reported that an earlier version of this worked on OpenRISC too. It's pretty easy for architectures to add support for this and just replace their private versions with the generic code. * generic-string-functions: sparc: use the new generic strnlen_user() function x86: use the new generic strnlen_user() function lib: add generic strnlen_user() function word-at-a-time: make the interfaces truly generic x86: use generic strncpy_from_user routine
Diffstat (limited to 'lib')
-rw-r--r--lib/Kconfig3
-rw-r--r--lib/Makefile1
-rw-r--r--lib/strncpy_from_user.c47
-rw-r--r--lib/strnlen_user.c138
4 files changed, 149 insertions, 40 deletions
diff --git a/lib/Kconfig b/lib/Kconfig
index 3e63af089082..a9e15403434e 100644
--- a/lib/Kconfig
+++ b/lib/Kconfig
@@ -19,6 +19,9 @@ config RATIONAL
19config GENERIC_STRNCPY_FROM_USER 19config GENERIC_STRNCPY_FROM_USER
20 bool 20 bool
21 21
22config GENERIC_STRNLEN_USER
23 bool
24
22config GENERIC_FIND_FIRST_BIT 25config GENERIC_FIND_FIRST_BIT
23 bool 26 bool
24 27
diff --git a/lib/Makefile b/lib/Makefile
index 2a1d7f9d39a2..8c31a0cb75e9 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -126,6 +126,7 @@ obj-$(CONFIG_CLZ_TAB) += clz_tab.o
126obj-$(CONFIG_DDR) += jedec_ddr_data.o 126obj-$(CONFIG_DDR) += jedec_ddr_data.o
127 127
128obj-$(CONFIG_GENERIC_STRNCPY_FROM_USER) += strncpy_from_user.o 128obj-$(CONFIG_GENERIC_STRNCPY_FROM_USER) += strncpy_from_user.o
129obj-$(CONFIG_GENERIC_STRNLEN_USER) += strnlen_user.o
129 130
130obj-$(CONFIG_STMP_DEVICE) += stmp_device.o 131obj-$(CONFIG_STMP_DEVICE) += stmp_device.o
131 132
diff --git a/lib/strncpy_from_user.c b/lib/strncpy_from_user.c
index c4c09b0e96ba..bb2b201d6ad0 100644
--- a/lib/strncpy_from_user.c
+++ b/lib/strncpy_from_user.c
@@ -4,37 +4,7 @@
4#include <linux/errno.h> 4#include <linux/errno.h>
5 5
6#include <asm/byteorder.h> 6#include <asm/byteorder.h>
7 7#include <asm/word-at-a-time.h>
8static inline long find_zero(unsigned long mask)
9{
10 long byte = 0;
11
12#ifdef __BIG_ENDIAN
13#ifdef CONFIG_64BIT
14 if (mask >> 32)
15 mask >>= 32;
16 else
17 byte = 4;
18#endif
19 if (mask >> 16)
20 mask >>= 16;
21 else
22 byte += 2;
23 return (mask >> 8) ? byte : byte + 1;
24#else
25#ifdef CONFIG_64BIT
26 if (!((unsigned int) mask)) {
27 mask >>= 32;
28 byte = 4;
29 }
30#endif
31 if (!(mask & 0xffff)) {
32 mask >>= 16;
33 byte += 2;
34 }
35 return (mask & 0xff) ? byte : byte + 1;
36#endif
37}
38 8
39#ifdef CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS 9#ifdef CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS
40#define IS_UNALIGNED(src, dst) 0 10#define IS_UNALIGNED(src, dst) 0
@@ -51,8 +21,7 @@ static inline long find_zero(unsigned long mask)
51 */ 21 */
52static inline long do_strncpy_from_user(char *dst, const char __user *src, long count, unsigned long max) 22static inline long do_strncpy_from_user(char *dst, const char __user *src, long count, unsigned long max)
53{ 23{
54 const unsigned long high_bits = REPEAT_BYTE(0xfe) + 1; 24 const struct word_at_a_time constants = WORD_AT_A_TIME_CONSTANTS;
55 const unsigned long low_bits = REPEAT_BYTE(0x7f);
56 long res = 0; 25 long res = 0;
57 26
58 /* 27 /*
@@ -66,18 +35,16 @@ static inline long do_strncpy_from_user(char *dst, const char __user *src, long
66 goto byte_at_a_time; 35 goto byte_at_a_time;
67 36
68 while (max >= sizeof(unsigned long)) { 37 while (max >= sizeof(unsigned long)) {
69 unsigned long c, v, rhs; 38 unsigned long c, data;
70 39
71 /* Fall back to byte-at-a-time if we get a page fault */ 40 /* Fall back to byte-at-a-time if we get a page fault */
72 if (unlikely(__get_user(c,(unsigned long __user *)(src+res)))) 41 if (unlikely(__get_user(c,(unsigned long __user *)(src+res))))
73 break; 42 break;
74 rhs = c | low_bits;
75 v = (c + high_bits) & ~rhs;
76 *(unsigned long *)(dst+res) = c; 43 *(unsigned long *)(dst+res) = c;
77 if (v) { 44 if (has_zero(c, &data, &constants)) {
78 v = (c & low_bits) + low_bits; 45 data = prep_zero_mask(c, data, &constants);
79 v = ~(v | rhs); 46 data = create_zero_mask(data);
80 return res + find_zero(v); 47 return res + find_zero(data);
81 } 48 }
82 res += sizeof(unsigned long); 49 res += sizeof(unsigned long);
83 max -= sizeof(unsigned long); 50 max -= sizeof(unsigned long);
diff --git a/lib/strnlen_user.c b/lib/strnlen_user.c
new file mode 100644
index 000000000000..90900ecfeb54
--- /dev/null
+++ b/lib/strnlen_user.c
@@ -0,0 +1,138 @@
1#include <linux/kernel.h>
2#include <linux/export.h>
3#include <linux/uaccess.h>
4
5#include <asm/word-at-a-time.h>
6
7/* Set bits in the first 'n' bytes when loaded from memory */
8#ifdef __LITTLE_ENDIAN
9# define aligned_byte_mask(n) ((1ul << 8*(n))-1)
10#else
11# define aligned_byte_mask(n) (~0xfful << 8*(7-(n)))
12#endif
13
14/*
15 * Do a strnlen, return length of string *with* final '\0'.
16 * 'count' is the user-supplied count, while 'max' is the
17 * address space maximum.
18 *
19 * Return 0 for exceptions (which includes hitting the address
20 * space maximum), or 'count+1' if hitting the user-supplied
21 * maximum count.
22 *
23 * NOTE! We can sometimes overshoot the user-supplied maximum
24 * if it fits in a aligned 'long'. The caller needs to check
25 * the return value against "> max".
26 */
27static inline long do_strnlen_user(const char __user *src, unsigned long count, unsigned long max)
28{
29 const struct word_at_a_time constants = WORD_AT_A_TIME_CONSTANTS;
30 long align, res = 0;
31 unsigned long c;
32
33 /*
34 * Truncate 'max' to the user-specified limit, so that
35 * we only have one limit we need to check in the loop
36 */
37 if (max > count)
38 max = count;
39
40 /*
41 * Do everything aligned. But that means that we
42 * need to also expand the maximum..
43 */
44 align = (sizeof(long) - 1) & (unsigned long)src;
45 src -= align;
46 max += align;
47
48 if (unlikely(__get_user(c,(unsigned long __user *)src)))
49 return 0;
50 c |= aligned_byte_mask(align);
51
52 for (;;) {
53 unsigned long data;
54 if (has_zero(c, &data, &constants)) {
55 data = prep_zero_mask(c, data, &constants);
56 data = create_zero_mask(data);
57 return res + find_zero(data) + 1 - align;
58 }
59 res += sizeof(unsigned long);
60 if (unlikely(max < sizeof(unsigned long)))
61 break;
62 max -= sizeof(unsigned long);
63 if (unlikely(__get_user(c,(unsigned long __user *)(src+res))))
64 return 0;
65 }
66 res -= align;
67
68 /*
69 * Uhhuh. We hit 'max'. But was that the user-specified maximum
70 * too? If so, return the marker for "too long".
71 */
72 if (res >= count)
73 return count+1;
74
75 /*
76 * Nope: we hit the address space limit, and we still had more
77 * characters the caller would have wanted. That's 0.
78 */
79 return 0;
80}
81
82/**
83 * strnlen_user: - Get the size of a user string INCLUDING final NUL.
84 * @str: The string to measure.
85 * @count: Maximum count (including NUL character)
86 *
87 * Context: User context only. This function may sleep.
88 *
89 * Get the size of a NUL-terminated string in user space.
90 *
91 * Returns the size of the string INCLUDING the terminating NUL.
92 * If the string is too long, returns 'count+1'.
93 * On exception (or invalid count), returns 0.
94 */
95long strnlen_user(const char __user *str, long count)
96{
97 unsigned long max_addr, src_addr;
98
99 if (unlikely(count <= 0))
100 return 0;
101
102 max_addr = user_addr_max();
103 src_addr = (unsigned long)str;
104 if (likely(src_addr < max_addr)) {
105 unsigned long max = max_addr - src_addr;
106 return do_strnlen_user(str, count, max);
107 }
108 return 0;
109}
110EXPORT_SYMBOL(strnlen_user);
111
112/**
113 * strlen_user: - Get the size of a user string INCLUDING final NUL.
114 * @str: The string to measure.
115 *
116 * Context: User context only. This function may sleep.
117 *
118 * Get the size of a NUL-terminated string in user space.
119 *
120 * Returns the size of the string INCLUDING the terminating NUL.
121 * On exception, returns 0.
122 *
123 * If there is a limit on the length of a valid string, you may wish to
124 * consider using strnlen_user() instead.
125 */
126long strlen_user(const char __user *str)
127{
128 unsigned long max_addr, src_addr;
129
130 max_addr = user_addr_max();
131 src_addr = (unsigned long)str;
132 if (likely(src_addr < max_addr)) {
133 unsigned long max = max_addr - src_addr;
134 return do_strnlen_user(str, ~0ul, max);
135 }
136 return 0;
137}
138EXPORT_SYMBOL(strlen_user);