summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMasami Hiramatsu <mhiramat@kernel.org>2019-05-15 01:38:18 -0400
committerSteven Rostedt (VMware) <rostedt@goodmis.org>2019-05-25 23:04:42 -0400
commit3d7081822f7f9eab867d9bcc8fd635208ec438e0 (patch)
tree8afb5e61c1f23305996d4a8e03dedd7c048de01d
parent2d8d8fac3b4eab035dcd0068e1f5a746a697fbb3 (diff)
uaccess: Add non-pagefault user-space read functions
Add probe_user_read(), strncpy_from_unsafe_user() and strnlen_unsafe_user() which allows caller to access user-space in IRQ context. Current probe_kernel_read() and strncpy_from_unsafe() are not available for user-space memory, because it sets KERNEL_DS while accessing data. On some arch, user address space and kernel address space can be co-exist, but others can not. In that case, setting KERNEL_DS means given address is treated as a kernel address space. Also strnlen_user() is only available from user context since it can sleep if pagefault is enabled. To access user-space memory without pagefault, we need these new functions which sets USER_DS while accessing the data. Link: http://lkml.kernel.org/r/155789869802.26965.4940338412595759063.stgit@devnote2 Acked-by: Ingo Molnar <mingo@kernel.org> Signed-off-by: Masami Hiramatsu <mhiramat@kernel.org> Signed-off-by: Steven Rostedt (VMware) <rostedt@goodmis.org>
-rw-r--r--include/linux/uaccess.h14
-rw-r--r--mm/maccess.c122
2 files changed, 130 insertions, 6 deletions
diff --git a/include/linux/uaccess.h b/include/linux/uaccess.h
index 5a43ef7db492..9c435c3f2105 100644
--- a/include/linux/uaccess.h
+++ b/include/linux/uaccess.h
@@ -243,6 +243,17 @@ extern long probe_kernel_read(void *dst, const void *src, size_t size);
243extern long __probe_kernel_read(void *dst, const void *src, size_t size); 243extern long __probe_kernel_read(void *dst, const void *src, size_t size);
244 244
245/* 245/*
246 * probe_user_read(): safely attempt to read from a location in user space
247 * @dst: pointer to the buffer that shall take the data
248 * @src: address to read from
249 * @size: size of the data chunk
250 *
251 * Safely read from address @src to the buffer at @dst. If a kernel fault
252 * happens, handle that and return -EFAULT.
253 */
254extern long probe_user_read(void *dst, const void __user *src, size_t size);
255
256/*
246 * probe_kernel_write(): safely attempt to write to a location 257 * probe_kernel_write(): safely attempt to write to a location
247 * @dst: address to write to 258 * @dst: address to write to
248 * @src: pointer to the data that shall be written 259 * @src: pointer to the data that shall be written
@@ -255,6 +266,9 @@ extern long notrace probe_kernel_write(void *dst, const void *src, size_t size);
255extern long notrace __probe_kernel_write(void *dst, const void *src, size_t size); 266extern long notrace __probe_kernel_write(void *dst, const void *src, size_t size);
256 267
257extern long strncpy_from_unsafe(char *dst, const void *unsafe_addr, long count); 268extern long strncpy_from_unsafe(char *dst, const void *unsafe_addr, long count);
269extern long strncpy_from_unsafe_user(char *dst, const void __user *unsafe_addr,
270 long count);
271extern long strnlen_unsafe_user(const void __user *unsafe_addr, long count);
258 272
259/** 273/**
260 * probe_kernel_address(): safely attempt to read from a location 274 * probe_kernel_address(): safely attempt to read from a location
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
8static __always_inline long
9probe_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}
40EXPORT_SYMBOL_GPL(probe_kernel_read); 49EXPORT_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
61long __weak probe_user_read(void *dst, const void __user *src, size_t size)
62 __attribute__((alias("__probe_user_read")));
63
64long __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}
76EXPORT_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}
67EXPORT_SYMBOL_GPL(probe_kernel_write); 103EXPORT_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 */
164long 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 */
205long 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}