Commit 2922585b93294d47172a765115e0dbc1bfe1be19
1 parent
446969084d
Exists in
master
and in
20 other branches
lib: Sparc's strncpy_from_user is generic enough, move under lib/
To use this, an architecture simply needs to: 1) Provide a user_addr_max() implementation via asm/uaccess.h 2) Add "select GENERIC_STRNCPY_FROM_USER" to their arch Kcnfig 3) Remove the existing strncpy_from_user() implementation and symbol exports their architecture had. Signed-off-by: David S. Miller <davem@davemloft.net> Acked-by: David Howells <dhowells@redhat.com>
Showing 5 changed files with 152 additions and 144 deletions Side-by-side Diff
arch/sparc/Kconfig
arch/sparc/lib/usercopy.c
1 | 1 | #include <linux/module.h> |
2 | -#include <linux/uaccess.h> | |
3 | 2 | #include <linux/kernel.h> |
4 | -#include <linux/errno.h> | |
5 | 3 | #include <linux/bug.h> |
6 | 4 | |
7 | -#include <asm/byteorder.h> | |
8 | - | |
9 | 5 | void copy_from_user_overflow(void) |
10 | 6 | { |
11 | 7 | WARN(1, "Buffer overflow detected!\n"); |
12 | 8 | } |
13 | 9 | EXPORT_SYMBOL(copy_from_user_overflow); |
14 | - | |
15 | -static inline long find_zero(unsigned long mask) | |
16 | -{ | |
17 | - long byte = 0; | |
18 | - | |
19 | -#ifdef __BIG_ENDIAN | |
20 | -#ifdef CONFIG_64BIT | |
21 | - if (mask >> 32) | |
22 | - mask >>= 32; | |
23 | - else | |
24 | - byte = 4; | |
25 | -#endif | |
26 | - if (mask >> 16) | |
27 | - mask >>= 16; | |
28 | - else | |
29 | - byte += 2; | |
30 | - return (mask >> 8) ? byte : byte + 1; | |
31 | -#else | |
32 | -#ifdef CONFIG_64BIT | |
33 | - if (!((unsigned int) mask)) { | |
34 | - mask >>= 32; | |
35 | - byte = 4; | |
36 | - } | |
37 | -#endif | |
38 | - if (!(mask & 0xffff)) { | |
39 | - mask >>= 16; | |
40 | - byte += 2; | |
41 | - } | |
42 | - return (mask & 0xff) ? byte : byte + 1; | |
43 | -#endif | |
44 | -} | |
45 | - | |
46 | -#ifdef CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS | |
47 | -#define IS_UNALIGNED(src, dst) 0 | |
48 | -#else | |
49 | -#define IS_UNALIGNED(src, dst) \ | |
50 | - (((long) dst | (long) src) & (sizeof(long) - 1)) | |
51 | -#endif | |
52 | - | |
53 | -/* | |
54 | - * Do a strncpy, return length of string without final '\0'. | |
55 | - * 'count' is the user-supplied count (return 'count' if we | |
56 | - * hit it), 'max' is the address space maximum (and we return | |
57 | - * -EFAULT if we hit it). | |
58 | - */ | |
59 | -static inline long do_strncpy_from_user(char *dst, const char __user *src, long count, unsigned long max) | |
60 | -{ | |
61 | - const unsigned long high_bits = REPEAT_BYTE(0xfe) + 1; | |
62 | - const unsigned long low_bits = REPEAT_BYTE(0x7f); | |
63 | - long res = 0; | |
64 | - | |
65 | - /* | |
66 | - * Truncate 'max' to the user-specified limit, so that | |
67 | - * we only have one limit we need to check in the loop | |
68 | - */ | |
69 | - if (max > count) | |
70 | - max = count; | |
71 | - | |
72 | - if (IS_UNALIGNED(src, dst)) | |
73 | - goto byte_at_a_time; | |
74 | - | |
75 | - while (max >= sizeof(unsigned long)) { | |
76 | - unsigned long c, v, rhs; | |
77 | - | |
78 | - /* Fall back to byte-at-a-time if we get a page fault */ | |
79 | - if (unlikely(__get_user(c,(unsigned long __user *)(src+res)))) | |
80 | - break; | |
81 | - rhs = c | low_bits; | |
82 | - v = (c + high_bits) & ~rhs; | |
83 | - *(unsigned long *)(dst+res) = c; | |
84 | - if (v) { | |
85 | - v = (c & low_bits) + low_bits; | |
86 | - v = ~(v | rhs); | |
87 | - return res + find_zero(v); | |
88 | - } | |
89 | - res += sizeof(unsigned long); | |
90 | - max -= sizeof(unsigned long); | |
91 | - } | |
92 | - | |
93 | -byte_at_a_time: | |
94 | - while (max) { | |
95 | - char c; | |
96 | - | |
97 | - if (unlikely(__get_user(c,src+res))) | |
98 | - return -EFAULT; | |
99 | - dst[res] = c; | |
100 | - if (!c) | |
101 | - return res; | |
102 | - res++; | |
103 | - max--; | |
104 | - } | |
105 | - | |
106 | - /* | |
107 | - * Uhhuh. We hit 'max'. But was that the user-specified maximum | |
108 | - * too? If so, that's ok - we got as much as the user asked for. | |
109 | - */ | |
110 | - if (res >= count) | |
111 | - return res; | |
112 | - | |
113 | - /* | |
114 | - * Nope: we hit the address space limit, and we still had more | |
115 | - * characters the caller would have wanted. That's an EFAULT. | |
116 | - */ | |
117 | - return -EFAULT; | |
118 | -} | |
119 | - | |
120 | -/** | |
121 | - * strncpy_from_user: - Copy a NUL terminated string from userspace. | |
122 | - * @dst: Destination address, in kernel space. This buffer must be at | |
123 | - * least @count bytes long. | |
124 | - * @src: Source address, in user space. | |
125 | - * @count: Maximum number of bytes to copy, including the trailing NUL. | |
126 | - * | |
127 | - * Copies a NUL-terminated string from userspace to kernel space. | |
128 | - * | |
129 | - * On success, returns the length of the string (not including the trailing | |
130 | - * NUL). | |
131 | - * | |
132 | - * If access to userspace fails, returns -EFAULT (some data may have been | |
133 | - * copied). | |
134 | - * | |
135 | - * If @count is smaller than the length of the string, copies @count bytes | |
136 | - * and returns @count. | |
137 | - */ | |
138 | -long strncpy_from_user(char *dst, const char __user *src, long count) | |
139 | -{ | |
140 | - unsigned long max_addr, src_addr; | |
141 | - | |
142 | - if (unlikely(count <= 0)) | |
143 | - return 0; | |
144 | - | |
145 | - max_addr = user_addr_max(); | |
146 | - src_addr = (unsigned long)src; | |
147 | - if (likely(src_addr < max_addr)) { | |
148 | - unsigned long max = max_addr - src_addr; | |
149 | - return do_strncpy_from_user(dst, src, count, max); | |
150 | - } | |
151 | - return -EFAULT; | |
152 | -} | |
153 | -EXPORT_SYMBOL(strncpy_from_user); |
lib/Kconfig
lib/Makefile
lib/strncpy_from_user.c
1 | +#include <linux/module.h> | |
2 | +#include <linux/uaccess.h> | |
3 | +#include <linux/kernel.h> | |
4 | +#include <linux/errno.h> | |
5 | + | |
6 | +#include <asm/byteorder.h> | |
7 | + | |
8 | +static 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 | + | |
39 | +#ifdef CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS | |
40 | +#define IS_UNALIGNED(src, dst) 0 | |
41 | +#else | |
42 | +#define IS_UNALIGNED(src, dst) \ | |
43 | + (((long) dst | (long) src) & (sizeof(long) - 1)) | |
44 | +#endif | |
45 | + | |
46 | +/* | |
47 | + * Do a strncpy, return length of string without final '\0'. | |
48 | + * 'count' is the user-supplied count (return 'count' if we | |
49 | + * hit it), 'max' is the address space maximum (and we return | |
50 | + * -EFAULT if we hit it). | |
51 | + */ | |
52 | +static inline long do_strncpy_from_user(char *dst, const char __user *src, long count, unsigned long max) | |
53 | +{ | |
54 | + const unsigned long high_bits = REPEAT_BYTE(0xfe) + 1; | |
55 | + const unsigned long low_bits = REPEAT_BYTE(0x7f); | |
56 | + long res = 0; | |
57 | + | |
58 | + /* | |
59 | + * Truncate 'max' to the user-specified limit, so that | |
60 | + * we only have one limit we need to check in the loop | |
61 | + */ | |
62 | + if (max > count) | |
63 | + max = count; | |
64 | + | |
65 | + if (IS_UNALIGNED(src, dst)) | |
66 | + goto byte_at_a_time; | |
67 | + | |
68 | + while (max >= sizeof(unsigned long)) { | |
69 | + unsigned long c, v, rhs; | |
70 | + | |
71 | + /* Fall back to byte-at-a-time if we get a page fault */ | |
72 | + if (unlikely(__get_user(c,(unsigned long __user *)(src+res)))) | |
73 | + break; | |
74 | + rhs = c | low_bits; | |
75 | + v = (c + high_bits) & ~rhs; | |
76 | + *(unsigned long *)(dst+res) = c; | |
77 | + if (v) { | |
78 | + v = (c & low_bits) + low_bits; | |
79 | + v = ~(v | rhs); | |
80 | + return res + find_zero(v); | |
81 | + } | |
82 | + res += sizeof(unsigned long); | |
83 | + max -= sizeof(unsigned long); | |
84 | + } | |
85 | + | |
86 | +byte_at_a_time: | |
87 | + while (max) { | |
88 | + char c; | |
89 | + | |
90 | + if (unlikely(__get_user(c,src+res))) | |
91 | + return -EFAULT; | |
92 | + dst[res] = c; | |
93 | + if (!c) | |
94 | + return res; | |
95 | + res++; | |
96 | + max--; | |
97 | + } | |
98 | + | |
99 | + /* | |
100 | + * Uhhuh. We hit 'max'. But was that the user-specified maximum | |
101 | + * too? If so, that's ok - we got as much as the user asked for. | |
102 | + */ | |
103 | + if (res >= count) | |
104 | + return res; | |
105 | + | |
106 | + /* | |
107 | + * Nope: we hit the address space limit, and we still had more | |
108 | + * characters the caller would have wanted. That's an EFAULT. | |
109 | + */ | |
110 | + return -EFAULT; | |
111 | +} | |
112 | + | |
113 | +/** | |
114 | + * strncpy_from_user: - Copy a NUL terminated string from userspace. | |
115 | + * @dst: Destination address, in kernel space. This buffer must be at | |
116 | + * least @count bytes long. | |
117 | + * @src: Source address, in user space. | |
118 | + * @count: Maximum number of bytes to copy, including the trailing NUL. | |
119 | + * | |
120 | + * Copies a NUL-terminated string from userspace to kernel space. | |
121 | + * | |
122 | + * On success, returns the length of the string (not including the trailing | |
123 | + * NUL). | |
124 | + * | |
125 | + * If access to userspace fails, returns -EFAULT (some data may have been | |
126 | + * copied). | |
127 | + * | |
128 | + * If @count is smaller than the length of the string, copies @count bytes | |
129 | + * and returns @count. | |
130 | + */ | |
131 | +long strncpy_from_user(char *dst, const char __user *src, long count) | |
132 | +{ | |
133 | + unsigned long max_addr, src_addr; | |
134 | + | |
135 | + if (unlikely(count <= 0)) | |
136 | + return 0; | |
137 | + | |
138 | + max_addr = user_addr_max(); | |
139 | + src_addr = (unsigned long)src; | |
140 | + if (likely(src_addr < max_addr)) { | |
141 | + unsigned long max = max_addr - src_addr; | |
142 | + return do_strncpy_from_user(dst, src, count, max); | |
143 | + } | |
144 | + return -EFAULT; | |
145 | +} | |
146 | +EXPORT_SYMBOL(strncpy_from_user); |