Skip to content

Commit 3d70818

Browse files
mhiramatrostedt
authored andcommitted
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 <[email protected]> Signed-off-by: Masami Hiramatsu <[email protected]> Signed-off-by: Steven Rostedt (VMware) <[email protected]>
1 parent 2d8d8fa commit 3d70818

File tree

2 files changed

+130
-6
lines changed

2 files changed

+130
-6
lines changed

include/linux/uaccess.h

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -242,6 +242,17 @@ static inline unsigned long __copy_from_user_inatomic_nocache(void *to,
242242
extern long probe_kernel_read(void *dst, const void *src, size_t size);
243243
extern long __probe_kernel_read(void *dst, const void *src, size_t size);
244244

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+
*/
254+
extern long probe_user_read(void *dst, const void __user *src, size_t size);
255+
245256
/*
246257
* probe_kernel_write(): safely attempt to write to a location
247258
* @dst: address to write to
@@ -255,6 +266,9 @@ extern long notrace probe_kernel_write(void *dst, const void *src, size_t size);
255266
extern long notrace __probe_kernel_write(void *dst, const void *src, size_t size);
256267

257268
extern long strncpy_from_unsafe(char *dst, const void *unsafe_addr, long count);
269+
extern long strncpy_from_unsafe_user(char *dst, const void __user *unsafe_addr,
270+
long count);
271+
extern long strnlen_unsafe_user(const void __user *unsafe_addr, long count);
258272

259273
/**
260274
* probe_kernel_address(): safely attempt to read from a location

mm/maccess.c

Lines changed: 116 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,20 @@
55
#include <linux/mm.h>
66
#include <linux/uaccess.h>
77

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+
820
/**
9-
* probe_kernel_read(): safely attempt to read from a location
21+
* probe_kernel_read(): safely attempt to read from a kernel-space location
1022
* @dst: pointer to the buffer that shall take the data
1123
* @src: address to read from
1224
* @size: size of the data chunk
@@ -29,16 +41,40 @@ long __probe_kernel_read(void *dst, const void *src, size_t size)
2941
mm_segment_t old_fs = get_fs();
3042

3143
set_fs(KERNEL_DS);
32-
pagefault_disable();
33-
ret = __copy_from_user_inatomic(dst,
34-
(__force const void __user *)src, size);
35-
pagefault_enable();
44+
ret = probe_read_common(dst, (__force const void __user *)src, size);
3645
set_fs(old_fs);
3746

38-
return ret ? -EFAULT : 0;
47+
return ret;
3948
}
4049
EXPORT_SYMBOL_GPL(probe_kernel_read);
4150

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+
4278
/**
4379
* probe_kernel_write(): safely attempt to write to a location
4480
* @dst: address to write to
@@ -66,6 +102,7 @@ long __probe_kernel_write(void *dst, const void *src, size_t size)
66102
}
67103
EXPORT_SYMBOL_GPL(probe_kernel_write);
68104

105+
69106
/**
70107
* strncpy_from_unsafe: - Copy a NUL terminated string from unsafe address.
71108
* @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)
105142

106143
return ret ? -EFAULT : src - unsafe_addr;
107144
}
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+
}

0 commit comments

Comments
 (0)