diff options
Diffstat (limited to 'mm/maccess.c')
-rw-r--r-- | mm/maccess.c | 122 |
1 files changed, 116 insertions, 6 deletions
diff --git a/mm/maccess.c b/mm/maccess.c index ec00be51a24f..19c8c3dc14df 100644 --- a/mm/maccess.c +++ b/mm/maccess.c | |||
@@ -5,8 +5,20 @@ | |||
5 | #include <linux/mm.h> | 5 | #include <linux/mm.h> |
6 | #include <linux/uaccess.h> | 6 | #include <linux/uaccess.h> |
7 | 7 | ||
8 | static __always_inline long | ||
9 | probe_read_common(void *dst, const void __user *src, size_t size) | ||
10 | { | ||
11 | long ret; | ||
12 | |||
13 | pagefault_disable(); | ||
14 | ret = __copy_from_user_inatomic(dst, src, size); | ||
15 | pagefault_enable(); | ||
16 | |||
17 | return ret ? -EFAULT : 0; | ||
18 | } | ||
19 | |||
8 | /** | 20 | /** |
9 | * probe_kernel_read(): safely attempt to read from a location | 21 | * probe_kernel_read(): safely attempt to read from a kernel-space location |
10 | * @dst: pointer to the buffer that shall take the data | 22 | * @dst: pointer to the buffer that shall take the data |
11 | * @src: address to read from | 23 | * @src: address to read from |
12 | * @size: size of the data chunk | 24 | * @size: size of the data chunk |
@@ -29,17 +41,41 @@ long __probe_kernel_read(void *dst, const void *src, size_t size) | |||
29 | mm_segment_t old_fs = get_fs(); | 41 | mm_segment_t old_fs = get_fs(); |
30 | 42 | ||
31 | set_fs(KERNEL_DS); | 43 | set_fs(KERNEL_DS); |
32 | pagefault_disable(); | 44 | ret = probe_read_common(dst, (__force const void __user *)src, size); |
33 | ret = __copy_from_user_inatomic(dst, | ||
34 | (__force const void __user *)src, size); | ||
35 | pagefault_enable(); | ||
36 | set_fs(old_fs); | 45 | set_fs(old_fs); |
37 | 46 | ||
38 | return ret ? -EFAULT : 0; | 47 | return ret; |
39 | } | 48 | } |
40 | EXPORT_SYMBOL_GPL(probe_kernel_read); | 49 | EXPORT_SYMBOL_GPL(probe_kernel_read); |
41 | 50 | ||
42 | /** | 51 | /** |
52 | * probe_user_read(): safely attempt to read from a user-space location | ||
53 | * @dst: pointer to the buffer that shall take the data | ||
54 | * @src: address to read from. This must be a user address. | ||
55 | * @size: size of the data chunk | ||
56 | * | ||
57 | * Safely read from user address @src to the buffer at @dst. If a kernel fault | ||
58 | * happens, handle that and return -EFAULT. | ||
59 | */ | ||
60 | |||
61 | long __weak probe_user_read(void *dst, const void __user *src, size_t size) | ||
62 | __attribute__((alias("__probe_user_read"))); | ||
63 | |||
64 | long __probe_user_read(void *dst, const void __user *src, size_t size) | ||
65 | { | ||
66 | long ret = -EFAULT; | ||
67 | mm_segment_t old_fs = get_fs(); | ||
68 | |||
69 | set_fs(USER_DS); | ||
70 | if (access_ok(src, size)) | ||
71 | ret = probe_read_common(dst, src, size); | ||
72 | set_fs(old_fs); | ||
73 | |||
74 | return ret; | ||
75 | } | ||
76 | EXPORT_SYMBOL_GPL(probe_user_read); | ||
77 | |||
78 | /** | ||
43 | * probe_kernel_write(): safely attempt to write to a location | 79 | * probe_kernel_write(): safely attempt to write to a location |
44 | * @dst: address to write to | 80 | * @dst: address to write to |
45 | * @src: pointer to the data that shall be written | 81 | * @src: pointer to the data that shall be written |
@@ -66,6 +102,7 @@ long __probe_kernel_write(void *dst, const void *src, size_t size) | |||
66 | } | 102 | } |
67 | EXPORT_SYMBOL_GPL(probe_kernel_write); | 103 | EXPORT_SYMBOL_GPL(probe_kernel_write); |
68 | 104 | ||
105 | |||
69 | /** | 106 | /** |
70 | * strncpy_from_unsafe: - Copy a NUL terminated string from unsafe address. | 107 | * strncpy_from_unsafe: - Copy a NUL terminated string from unsafe address. |
71 | * @dst: Destination address, in kernel space. This buffer must be at | 108 | * @dst: Destination address, in kernel space. This buffer must be at |
@@ -105,3 +142,76 @@ long strncpy_from_unsafe(char *dst, const void *unsafe_addr, long count) | |||
105 | 142 | ||
106 | return ret ? -EFAULT : src - unsafe_addr; | 143 | return ret ? -EFAULT : src - unsafe_addr; |
107 | } | 144 | } |
145 | |||
146 | /** | ||
147 | * strncpy_from_unsafe_user: - Copy a NUL terminated string from unsafe user | ||
148 | * address. | ||
149 | * @dst: Destination address, in kernel space. This buffer must be at | ||
150 | * least @count bytes long. | ||
151 | * @unsafe_addr: Unsafe user address. | ||
152 | * @count: Maximum number of bytes to copy, including the trailing NUL. | ||
153 | * | ||
154 | * Copies a NUL-terminated string from unsafe user address to kernel buffer. | ||
155 | * | ||
156 | * On success, returns the length of the string INCLUDING the trailing NUL. | ||
157 | * | ||
158 | * If access fails, returns -EFAULT (some data may have been copied | ||
159 | * and the trailing NUL added). | ||
160 | * | ||
161 | * If @count is smaller than the length of the string, copies @count-1 bytes, | ||
162 | * sets the last byte of @dst buffer to NUL and returns @count. | ||
163 | */ | ||
164 | long strncpy_from_unsafe_user(char *dst, const void __user *unsafe_addr, | ||
165 | long count) | ||
166 | { | ||
167 | mm_segment_t old_fs = get_fs(); | ||
168 | long ret; | ||
169 | |||
170 | if (unlikely(count <= 0)) | ||
171 | return 0; | ||
172 | |||
173 | set_fs(USER_DS); | ||
174 | pagefault_disable(); | ||
175 | ret = strncpy_from_user(dst, unsafe_addr, count); | ||
176 | pagefault_enable(); | ||
177 | set_fs(old_fs); | ||
178 | |||
179 | if (ret >= count) { | ||
180 | ret = count; | ||
181 | dst[ret - 1] = '\0'; | ||
182 | } else if (ret > 0) { | ||
183 | ret++; | ||
184 | } | ||
185 | |||
186 | return ret; | ||
187 | } | ||
188 | |||
189 | /** | ||
190 | * strnlen_unsafe_user: - Get the size of a user string INCLUDING final NUL. | ||
191 | * @unsafe_addr: The string to measure. | ||
192 | * @count: Maximum count (including NUL) | ||
193 | * | ||
194 | * Get the size of a NUL-terminated string in user space without pagefault. | ||
195 | * | ||
196 | * Returns the size of the string INCLUDING the terminating NUL. | ||
197 | * | ||
198 | * If the string is too long, returns a number larger than @count. User | ||
199 | * has to check the return value against "> count". | ||
200 | * On exception (or invalid count), returns 0. | ||
201 | * | ||
202 | * Unlike strnlen_user, this can be used from IRQ handler etc. because | ||
203 | * it disables pagefaults. | ||
204 | */ | ||
205 | long strnlen_unsafe_user(const void __user *unsafe_addr, long count) | ||
206 | { | ||
207 | mm_segment_t old_fs = get_fs(); | ||
208 | int ret; | ||
209 | |||
210 | set_fs(USER_DS); | ||
211 | pagefault_disable(); | ||
212 | ret = strnlen_user(unsafe_addr, count); | ||
213 | pagefault_enable(); | ||
214 | set_fs(old_fs); | ||
215 | |||
216 | return ret; | ||
217 | } | ||