Blame view

mm/maccess.c 8.68 KB
457c89965   Thomas Gleixner   treewide: Add SPD...
1
  // SPDX-License-Identifier: GPL-2.0-only
c33fa9f56   Ingo Molnar   uaccess: add prob...
2
  /*
3f0acb1eb   Christoph Hellwig   maccess: update t...
3
   * Access kernel or user memory without faulting.
c33fa9f56   Ingo Molnar   uaccess: add prob...
4
   */
b95f1b31b   Paul Gortmaker   mm: Map most file...
5
  #include <linux/export.h>
c33fa9f56   Ingo Molnar   uaccess: add prob...
6
  #include <linux/mm.h>
7c7fcf762   David Howells   MN10300: Save fra...
7
  #include <linux/uaccess.h>
c33fa9f56   Ingo Molnar   uaccess: add prob...
8

fe557319a   Christoph Hellwig   maccess: rename p...
9
10
  bool __weak copy_from_kernel_nofault_allowed(const void *unsafe_src,
  		size_t size)
eab0c6089   Christoph Hellwig   maccess: unify th...
11
12
13
  {
  	return true;
  }
b58294ead   Christoph Hellwig   maccess: allow ar...
14
  #ifdef HAVE_GET_KERNEL_NOFAULT
fe557319a   Christoph Hellwig   maccess: rename p...
15
  #define copy_from_kernel_nofault_loop(dst, src, len, type, err_label)	\
b58294ead   Christoph Hellwig   maccess: allow ar...
16
17
18
19
20
21
  	while (len >= sizeof(type)) {					\
  		__get_kernel_nofault(dst, src, type, err_label);		\
  		dst += sizeof(type);					\
  		src += sizeof(type);					\
  		len -= sizeof(type);					\
  	}
fe557319a   Christoph Hellwig   maccess: rename p...
22
  long copy_from_kernel_nofault(void *dst, const void *src, size_t size)
b58294ead   Christoph Hellwig   maccess: allow ar...
23
  {
fe557319a   Christoph Hellwig   maccess: rename p...
24
  	if (!copy_from_kernel_nofault_allowed(src, size))
2a71e81d3   Christoph Hellwig   maccess: return -...
25
  		return -ERANGE;
b58294ead   Christoph Hellwig   maccess: allow ar...
26
27
  
  	pagefault_disable();
fe557319a   Christoph Hellwig   maccess: rename p...
28
29
30
31
  	copy_from_kernel_nofault_loop(dst, src, size, u64, Efault);
  	copy_from_kernel_nofault_loop(dst, src, size, u32, Efault);
  	copy_from_kernel_nofault_loop(dst, src, size, u16, Efault);
  	copy_from_kernel_nofault_loop(dst, src, size, u8, Efault);
b58294ead   Christoph Hellwig   maccess: allow ar...
32
33
34
35
36
37
  	pagefault_enable();
  	return 0;
  Efault:
  	pagefault_enable();
  	return -EFAULT;
  }
fe557319a   Christoph Hellwig   maccess: rename p...
38
  EXPORT_SYMBOL_GPL(copy_from_kernel_nofault);
b58294ead   Christoph Hellwig   maccess: allow ar...
39

fe557319a   Christoph Hellwig   maccess: rename p...
40
  #define copy_to_kernel_nofault_loop(dst, src, len, type, err_label)	\
b58294ead   Christoph Hellwig   maccess: allow ar...
41
42
43
44
45
46
  	while (len >= sizeof(type)) {					\
  		__put_kernel_nofault(dst, src, type, err_label);		\
  		dst += sizeof(type);					\
  		src += sizeof(type);					\
  		len -= sizeof(type);					\
  	}
fe557319a   Christoph Hellwig   maccess: rename p...
47
  long copy_to_kernel_nofault(void *dst, const void *src, size_t size)
b58294ead   Christoph Hellwig   maccess: allow ar...
48
49
  {
  	pagefault_disable();
fe557319a   Christoph Hellwig   maccess: rename p...
50
51
52
53
  	copy_to_kernel_nofault_loop(dst, src, size, u64, Efault);
  	copy_to_kernel_nofault_loop(dst, src, size, u32, Efault);
  	copy_to_kernel_nofault_loop(dst, src, size, u16, Efault);
  	copy_to_kernel_nofault_loop(dst, src, size, u8, Efault);
b58294ead   Christoph Hellwig   maccess: allow ar...
54
55
56
57
58
59
60
61
62
63
64
65
66
  	pagefault_enable();
  	return 0;
  Efault:
  	pagefault_enable();
  	return -EFAULT;
  }
  
  long strncpy_from_kernel_nofault(char *dst, const void *unsafe_addr, long count)
  {
  	const void *src = unsafe_addr;
  
  	if (unlikely(count <= 0))
  		return 0;
fe557319a   Christoph Hellwig   maccess: rename p...
67
  	if (!copy_from_kernel_nofault_allowed(unsafe_addr, count))
2a71e81d3   Christoph Hellwig   maccess: return -...
68
  		return -ERANGE;
b58294ead   Christoph Hellwig   maccess: allow ar...
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
  
  	pagefault_disable();
  	do {
  		__get_kernel_nofault(dst, src, u8, Efault);
  		dst++;
  		src++;
  	} while (dst[-1] && src - unsafe_addr < count);
  	pagefault_enable();
  
  	dst[-1] = '\0';
  	return src - unsafe_addr;
  Efault:
  	pagefault_enable();
  	dst[-1] = '\0';
  	return -EFAULT;
  }
  #else /* HAVE_GET_KERNEL_NOFAULT */
c33fa9f56   Ingo Molnar   uaccess: add prob...
86
  /**
fe557319a   Christoph Hellwig   maccess: rename p...
87
   * copy_from_kernel_nofault(): safely attempt to read from kernel-space
4f6de12b3   Christoph Hellwig   maccess: clarify ...
88
89
90
91
92
   * @dst: pointer to the buffer that shall take the data
   * @src: address to read from
   * @size: size of the data chunk
   *
   * Safely read from kernel address @src to the buffer at @dst.  If a kernel
2a71e81d3   Christoph Hellwig   maccess: return -...
93
94
   * fault happens, handle that and return -EFAULT.  If @src is not a valid kernel
   * address, return -ERANGE.
0ab32b6f1   Andrew Morton   uaccess: reimplem...
95
96
   *
   * We ensure that the copy_from_user is executed in atomic context so that
c1e8d7c6a   Michel Lespinasse   mmap locking API:...
97
   * do_page_fault() doesn't attempt to take mmap_lock.  This makes
fe557319a   Christoph Hellwig   maccess: rename p...
98
   * copy_from_kernel_nofault() suitable for use within regions where the caller
c1e8d7c6a   Michel Lespinasse   mmap locking API:...
99
   * already holds mmap_lock, or other locks which nest inside mmap_lock.
c33fa9f56   Ingo Molnar   uaccess: add prob...
100
   */
fe557319a   Christoph Hellwig   maccess: rename p...
101
  long copy_from_kernel_nofault(void *dst, const void *src, size_t size)
c33fa9f56   Ingo Molnar   uaccess: add prob...
102
103
  {
  	long ret;
b4b8ac524   Jason Wessel   kgdb: fix optiona...
104
  	mm_segment_t old_fs = get_fs();
c33fa9f56   Ingo Molnar   uaccess: add prob...
105

fe557319a   Christoph Hellwig   maccess: rename p...
106
  	if (!copy_from_kernel_nofault_allowed(src, size))
2a71e81d3   Christoph Hellwig   maccess: return -...
107
  		return -ERANGE;
eab0c6089   Christoph Hellwig   maccess: unify th...
108

b4b8ac524   Jason Wessel   kgdb: fix optiona...
109
  	set_fs(KERNEL_DS);
cd0309058   Christoph Hellwig   maccess: remove p...
110
111
112
113
  	pagefault_disable();
  	ret = __copy_from_user_inatomic(dst, (__force const void __user *)src,
  			size);
  	pagefault_enable();
b4b8ac524   Jason Wessel   kgdb: fix optiona...
114
  	set_fs(old_fs);
c33fa9f56   Ingo Molnar   uaccess: add prob...
115

cd0309058   Christoph Hellwig   maccess: remove p...
116
117
118
  	if (ret)
  		return -EFAULT;
  	return 0;
c33fa9f56   Ingo Molnar   uaccess: add prob...
119
  }
fe557319a   Christoph Hellwig   maccess: rename p...
120
  EXPORT_SYMBOL_GPL(copy_from_kernel_nofault);
c33fa9f56   Ingo Molnar   uaccess: add prob...
121
122
  
  /**
fe557319a   Christoph Hellwig   maccess: rename p...
123
   * copy_to_kernel_nofault(): safely attempt to write to a location
c33fa9f56   Ingo Molnar   uaccess: add prob...
124
125
126
127
128
129
130
   * @dst: address to write to
   * @src: pointer to the data that shall be written
   * @size: size of the data chunk
   *
   * Safely write to address @dst from the buffer at @src.  If a kernel fault
   * happens, handle that and return -EFAULT.
   */
fe557319a   Christoph Hellwig   maccess: rename p...
131
  long copy_to_kernel_nofault(void *dst, const void *src, size_t size)
c33fa9f56   Ingo Molnar   uaccess: add prob...
132
133
  {
  	long ret;
b4b8ac524   Jason Wessel   kgdb: fix optiona...
134
  	mm_segment_t old_fs = get_fs();
c33fa9f56   Ingo Molnar   uaccess: add prob...
135

b4b8ac524   Jason Wessel   kgdb: fix optiona...
136
  	set_fs(KERNEL_DS);
cd0309058   Christoph Hellwig   maccess: remove p...
137
138
139
  	pagefault_disable();
  	ret = __copy_to_user_inatomic((__force void __user *)dst, src, size);
  	pagefault_enable();
b4b8ac524   Jason Wessel   kgdb: fix optiona...
140
  	set_fs(old_fs);
c33fa9f56   Ingo Molnar   uaccess: add prob...
141

cd0309058   Christoph Hellwig   maccess: remove p...
142
143
144
  	if (ret)
  		return -EFAULT;
  	return 0;
c33fa9f56   Ingo Molnar   uaccess: add prob...
145
  }
dbb7ee0e4   Alexei Starovoitov   lib: move strncpy...
146

1d1585ca0   Daniel Borkmann   uaccess: Add non-...
147
  /**
c4cb16442   Christoph Hellwig   maccess: rename s...
148
   * strncpy_from_kernel_nofault: - Copy a NUL terminated string from unsafe
4f6de12b3   Christoph Hellwig   maccess: clarify ...
149
150
151
152
153
154
155
156
157
158
   *				 address.
   * @dst:   Destination address, in kernel space.  This buffer must be at
   *         least @count bytes long.
   * @unsafe_addr: Unsafe address.
   * @count: Maximum number of bytes to copy, including the trailing NUL.
   *
   * Copies a NUL-terminated string from unsafe address to kernel buffer.
   *
   * On success, returns the length of the string INCLUDING the trailing NUL.
   *
2a71e81d3   Christoph Hellwig   maccess: return -...
159
160
161
   * If access fails, returns -EFAULT (some data may have been copied and the
   * trailing NUL added).  If @unsafe_addr is not a valid kernel address, return
   * -ERANGE.
4f6de12b3   Christoph Hellwig   maccess: clarify ...
162
163
164
165
   *
   * If @count is smaller than the length of the string, copies @count-1 bytes,
   * sets the last byte of @dst buffer to NUL and returns @count.
   */
eab0c6089   Christoph Hellwig   maccess: unify th...
166
167
  long strncpy_from_kernel_nofault(char *dst, const void *unsafe_addr, long count)
  {
dbb7ee0e4   Alexei Starovoitov   lib: move strncpy...
168
169
170
171
172
173
  	mm_segment_t old_fs = get_fs();
  	const void *src = unsafe_addr;
  	long ret;
  
  	if (unlikely(count <= 0))
  		return 0;
fe557319a   Christoph Hellwig   maccess: rename p...
174
  	if (!copy_from_kernel_nofault_allowed(unsafe_addr, count))
2a71e81d3   Christoph Hellwig   maccess: return -...
175
  		return -ERANGE;
dbb7ee0e4   Alexei Starovoitov   lib: move strncpy...
176
177
178
179
180
  
  	set_fs(KERNEL_DS);
  	pagefault_disable();
  
  	do {
bd28b1459   Linus Torvalds   x86: remove more ...
181
  		ret = __get_user(*dst++, (const char __user __force *)src++);
dbb7ee0e4   Alexei Starovoitov   lib: move strncpy...
182
183
184
185
186
  	} while (dst[-1] && ret == 0 && src - unsafe_addr < count);
  
  	dst[-1] = '\0';
  	pagefault_enable();
  	set_fs(old_fs);
9dd861d55   Rasmus Villemoes   mm/maccess.c: act...
187
  	return ret ? -EFAULT : src - unsafe_addr;
dbb7ee0e4   Alexei Starovoitov   lib: move strncpy...
188
  }
b58294ead   Christoph Hellwig   maccess: allow ar...
189
  #endif /* HAVE_GET_KERNEL_NOFAULT */
3d7081822   Masami Hiramatsu   uaccess: Add non-...
190
191
  
  /**
c0ee37e85   Christoph Hellwig   maccess: rename p...
192
   * copy_from_user_nofault(): safely attempt to read from a user-space location
fc3562d79   Christoph Hellwig   maccess: move use...
193
194
195
196
197
198
199
   * @dst: pointer to the buffer that shall take the data
   * @src: address to read from. This must be a user address.
   * @size: size of the data chunk
   *
   * Safely read from user address @src to the buffer at @dst. If a kernel fault
   * happens, handle that and return -EFAULT.
   */
c0ee37e85   Christoph Hellwig   maccess: rename p...
200
  long copy_from_user_nofault(void *dst, const void __user *src, size_t size)
fc3562d79   Christoph Hellwig   maccess: move use...
201
202
  {
  	long ret = -EFAULT;
3d13f313c   Christoph Hellwig   uaccess: add forc...
203
  	mm_segment_t old_fs = force_uaccess_begin();
fc3562d79   Christoph Hellwig   maccess: move use...
204

fc3562d79   Christoph Hellwig   maccess: move use...
205
206
207
208
209
  	if (access_ok(src, size)) {
  		pagefault_disable();
  		ret = __copy_from_user_inatomic(dst, src, size);
  		pagefault_enable();
  	}
3d13f313c   Christoph Hellwig   uaccess: add forc...
210
  	force_uaccess_end(old_fs);
fc3562d79   Christoph Hellwig   maccess: move use...
211
212
213
214
215
  
  	if (ret)
  		return -EFAULT;
  	return 0;
  }
c0ee37e85   Christoph Hellwig   maccess: rename p...
216
  EXPORT_SYMBOL_GPL(copy_from_user_nofault);
fc3562d79   Christoph Hellwig   maccess: move use...
217
218
  
  /**
c0ee37e85   Christoph Hellwig   maccess: rename p...
219
   * copy_to_user_nofault(): safely attempt to write to a user-space location
fc3562d79   Christoph Hellwig   maccess: move use...
220
221
222
223
224
225
226
   * @dst: address to write to
   * @src: pointer to the data that shall be written
   * @size: size of the data chunk
   *
   * Safely write to address @dst from the buffer at @src.  If a kernel fault
   * happens, handle that and return -EFAULT.
   */
c0ee37e85   Christoph Hellwig   maccess: rename p...
227
  long copy_to_user_nofault(void __user *dst, const void *src, size_t size)
fc3562d79   Christoph Hellwig   maccess: move use...
228
229
  {
  	long ret = -EFAULT;
3d13f313c   Christoph Hellwig   uaccess: add forc...
230
  	mm_segment_t old_fs = force_uaccess_begin();
fc3562d79   Christoph Hellwig   maccess: move use...
231

fc3562d79   Christoph Hellwig   maccess: move use...
232
233
234
235
236
  	if (access_ok(dst, size)) {
  		pagefault_disable();
  		ret = __copy_to_user_inatomic(dst, src, size);
  		pagefault_enable();
  	}
3d13f313c   Christoph Hellwig   uaccess: add forc...
237
  	force_uaccess_end(old_fs);
fc3562d79   Christoph Hellwig   maccess: move use...
238
239
240
241
242
  
  	if (ret)
  		return -EFAULT;
  	return 0;
  }
c0ee37e85   Christoph Hellwig   maccess: rename p...
243
  EXPORT_SYMBOL_GPL(copy_to_user_nofault);
fc3562d79   Christoph Hellwig   maccess: move use...
244
245
  
  /**
bd88bb5d4   Christoph Hellwig   maccess: rename s...
246
   * strncpy_from_user_nofault: - Copy a NUL terminated string from unsafe user
3d7081822   Masami Hiramatsu   uaccess: Add non-...
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
   *				address.
   * @dst:   Destination address, in kernel space.  This buffer must be at
   *         least @count bytes long.
   * @unsafe_addr: Unsafe user address.
   * @count: Maximum number of bytes to copy, including the trailing NUL.
   *
   * Copies a NUL-terminated string from unsafe user address to kernel buffer.
   *
   * On success, returns the length of the string INCLUDING the trailing NUL.
   *
   * If access fails, returns -EFAULT (some data may have been copied
   * and the trailing NUL added).
   *
   * If @count is smaller than the length of the string, copies @count-1 bytes,
   * sets the last byte of @dst buffer to NUL and returns @count.
   */
bd88bb5d4   Christoph Hellwig   maccess: rename s...
263
  long strncpy_from_user_nofault(char *dst, const void __user *unsafe_addr,
3d7081822   Masami Hiramatsu   uaccess: Add non-...
264
265
  			      long count)
  {
3d13f313c   Christoph Hellwig   uaccess: add forc...
266
  	mm_segment_t old_fs;
3d7081822   Masami Hiramatsu   uaccess: Add non-...
267
268
269
270
  	long ret;
  
  	if (unlikely(count <= 0))
  		return 0;
3d13f313c   Christoph Hellwig   uaccess: add forc...
271
  	old_fs = force_uaccess_begin();
3d7081822   Masami Hiramatsu   uaccess: Add non-...
272
273
274
  	pagefault_disable();
  	ret = strncpy_from_user(dst, unsafe_addr, count);
  	pagefault_enable();
3d13f313c   Christoph Hellwig   uaccess: add forc...
275
  	force_uaccess_end(old_fs);
3d7081822   Masami Hiramatsu   uaccess: Add non-...
276
277
278
279
280
281
282
283
284
285
286
287
  
  	if (ret >= count) {
  		ret = count;
  		dst[ret - 1] = '\0';
  	} else if (ret > 0) {
  		ret++;
  	}
  
  	return ret;
  }
  
  /**
02dddb160   Christoph Hellwig   maccess: rename s...
288
   * strnlen_user_nofault: - Get the size of a user string INCLUDING final NUL.
3d7081822   Masami Hiramatsu   uaccess: Add non-...
289
290
291
292
293
294
295
296
297
298
299
300
301
302
   * @unsafe_addr: The string to measure.
   * @count: Maximum count (including NUL)
   *
   * Get the size of a NUL-terminated string in user space without pagefault.
   *
   * Returns the size of the string INCLUDING the terminating NUL.
   *
   * If the string is too long, returns a number larger than @count. User
   * has to check the return value against "> count".
   * On exception (or invalid count), returns 0.
   *
   * Unlike strnlen_user, this can be used from IRQ handler etc. because
   * it disables pagefaults.
   */
02dddb160   Christoph Hellwig   maccess: rename s...
303
  long strnlen_user_nofault(const void __user *unsafe_addr, long count)
3d7081822   Masami Hiramatsu   uaccess: Add non-...
304
  {
3d13f313c   Christoph Hellwig   uaccess: add forc...
305
  	mm_segment_t old_fs;
3d7081822   Masami Hiramatsu   uaccess: Add non-...
306
  	int ret;
3d13f313c   Christoph Hellwig   uaccess: add forc...
307
  	old_fs = force_uaccess_begin();
3d7081822   Masami Hiramatsu   uaccess: Add non-...
308
309
310
  	pagefault_disable();
  	ret = strnlen_user(unsafe_addr, count);
  	pagefault_enable();
3d13f313c   Christoph Hellwig   uaccess: add forc...
311
  	force_uaccess_end(old_fs);
3d7081822   Masami Hiramatsu   uaccess: Add non-...
312
313
314
  
  	return ret;
  }