Blame view
mm/maccess.c
8.68 KB
457c89965 treewide: Add SPD... |
1 |
// SPDX-License-Identifier: GPL-2.0-only |
c33fa9f56 uaccess: add prob... |
2 |
/* |
3f0acb1eb maccess: update t... |
3 |
* Access kernel or user memory without faulting. |
c33fa9f56 uaccess: add prob... |
4 |
*/ |
b95f1b31b mm: Map most file... |
5 |
#include <linux/export.h> |
c33fa9f56 uaccess: add prob... |
6 |
#include <linux/mm.h> |
7c7fcf762 MN10300: Save fra... |
7 |
#include <linux/uaccess.h> |
c33fa9f56 uaccess: add prob... |
8 |
|
fe557319a maccess: rename p... |
9 10 |
bool __weak copy_from_kernel_nofault_allowed(const void *unsafe_src, size_t size) |
eab0c6089 maccess: unify th... |
11 12 13 |
{ return true; } |
b58294ead maccess: allow ar... |
14 |
#ifdef HAVE_GET_KERNEL_NOFAULT |
fe557319a maccess: rename p... |
15 |
#define copy_from_kernel_nofault_loop(dst, src, len, type, err_label) \ |
b58294ead 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 maccess: rename p... |
22 |
long copy_from_kernel_nofault(void *dst, const void *src, size_t size) |
b58294ead maccess: allow ar... |
23 |
{ |
fe557319a maccess: rename p... |
24 |
if (!copy_from_kernel_nofault_allowed(src, size)) |
2a71e81d3 maccess: return -... |
25 |
return -ERANGE; |
b58294ead maccess: allow ar... |
26 27 |
pagefault_disable(); |
fe557319a 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 maccess: allow ar... |
32 33 34 35 36 37 |
pagefault_enable(); return 0; Efault: pagefault_enable(); return -EFAULT; } |
fe557319a maccess: rename p... |
38 |
EXPORT_SYMBOL_GPL(copy_from_kernel_nofault); |
b58294ead maccess: allow ar... |
39 |
|
fe557319a maccess: rename p... |
40 |
#define copy_to_kernel_nofault_loop(dst, src, len, type, err_label) \ |
b58294ead 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 maccess: rename p... |
47 |
long copy_to_kernel_nofault(void *dst, const void *src, size_t size) |
b58294ead maccess: allow ar... |
48 49 |
{ pagefault_disable(); |
fe557319a 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 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 maccess: rename p... |
67 |
if (!copy_from_kernel_nofault_allowed(unsafe_addr, count)) |
2a71e81d3 maccess: return -... |
68 |
return -ERANGE; |
b58294ead 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 uaccess: add prob... |
86 |
/** |
fe557319a maccess: rename p... |
87 |
* copy_from_kernel_nofault(): safely attempt to read from kernel-space |
4f6de12b3 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 maccess: return -... |
93 94 |
* fault happens, handle that and return -EFAULT. If @src is not a valid kernel * address, return -ERANGE. |
0ab32b6f1 uaccess: reimplem... |
95 96 |
* * We ensure that the copy_from_user is executed in atomic context so that |
c1e8d7c6a mmap locking API:... |
97 |
* do_page_fault() doesn't attempt to take mmap_lock. This makes |
fe557319a maccess: rename p... |
98 |
* copy_from_kernel_nofault() suitable for use within regions where the caller |
c1e8d7c6a mmap locking API:... |
99 |
* already holds mmap_lock, or other locks which nest inside mmap_lock. |
c33fa9f56 uaccess: add prob... |
100 |
*/ |
fe557319a maccess: rename p... |
101 |
long copy_from_kernel_nofault(void *dst, const void *src, size_t size) |
c33fa9f56 uaccess: add prob... |
102 103 |
{ long ret; |
b4b8ac524 kgdb: fix optiona... |
104 |
mm_segment_t old_fs = get_fs(); |
c33fa9f56 uaccess: add prob... |
105 |
|
fe557319a maccess: rename p... |
106 |
if (!copy_from_kernel_nofault_allowed(src, size)) |
2a71e81d3 maccess: return -... |
107 |
return -ERANGE; |
eab0c6089 maccess: unify th... |
108 |
|
b4b8ac524 kgdb: fix optiona... |
109 |
set_fs(KERNEL_DS); |
cd0309058 maccess: remove p... |
110 111 112 113 |
pagefault_disable(); ret = __copy_from_user_inatomic(dst, (__force const void __user *)src, size); pagefault_enable(); |
b4b8ac524 kgdb: fix optiona... |
114 |
set_fs(old_fs); |
c33fa9f56 uaccess: add prob... |
115 |
|
cd0309058 maccess: remove p... |
116 117 118 |
if (ret) return -EFAULT; return 0; |
c33fa9f56 uaccess: add prob... |
119 |
} |
fe557319a maccess: rename p... |
120 |
EXPORT_SYMBOL_GPL(copy_from_kernel_nofault); |
c33fa9f56 uaccess: add prob... |
121 122 |
/** |
fe557319a maccess: rename p... |
123 |
* copy_to_kernel_nofault(): safely attempt to write to a location |
c33fa9f56 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 maccess: rename p... |
131 |
long copy_to_kernel_nofault(void *dst, const void *src, size_t size) |
c33fa9f56 uaccess: add prob... |
132 133 |
{ long ret; |
b4b8ac524 kgdb: fix optiona... |
134 |
mm_segment_t old_fs = get_fs(); |
c33fa9f56 uaccess: add prob... |
135 |
|
b4b8ac524 kgdb: fix optiona... |
136 |
set_fs(KERNEL_DS); |
cd0309058 maccess: remove p... |
137 138 139 |
pagefault_disable(); ret = __copy_to_user_inatomic((__force void __user *)dst, src, size); pagefault_enable(); |
b4b8ac524 kgdb: fix optiona... |
140 |
set_fs(old_fs); |
c33fa9f56 uaccess: add prob... |
141 |
|
cd0309058 maccess: remove p... |
142 143 144 |
if (ret) return -EFAULT; return 0; |
c33fa9f56 uaccess: add prob... |
145 |
} |
dbb7ee0e4 lib: move strncpy... |
146 |
|
1d1585ca0 uaccess: Add non-... |
147 |
/** |
c4cb16442 maccess: rename s... |
148 |
* strncpy_from_kernel_nofault: - Copy a NUL terminated string from unsafe |
4f6de12b3 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 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 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 maccess: unify th... |
166 167 |
long strncpy_from_kernel_nofault(char *dst, const void *unsafe_addr, long count) { |
dbb7ee0e4 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 maccess: rename p... |
174 |
if (!copy_from_kernel_nofault_allowed(unsafe_addr, count)) |
2a71e81d3 maccess: return -... |
175 |
return -ERANGE; |
dbb7ee0e4 lib: move strncpy... |
176 177 178 179 180 |
set_fs(KERNEL_DS); pagefault_disable(); do { |
bd28b1459 x86: remove more ... |
181 |
ret = __get_user(*dst++, (const char __user __force *)src++); |
dbb7ee0e4 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 mm/maccess.c: act... |
187 |
return ret ? -EFAULT : src - unsafe_addr; |
dbb7ee0e4 lib: move strncpy... |
188 |
} |
b58294ead maccess: allow ar... |
189 |
#endif /* HAVE_GET_KERNEL_NOFAULT */ |
3d7081822 uaccess: Add non-... |
190 191 |
/** |
c0ee37e85 maccess: rename p... |
192 |
* copy_from_user_nofault(): safely attempt to read from a user-space location |
fc3562d79 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 maccess: rename p... |
200 |
long copy_from_user_nofault(void *dst, const void __user *src, size_t size) |
fc3562d79 maccess: move use... |
201 202 |
{ long ret = -EFAULT; |
3d13f313c uaccess: add forc... |
203 |
mm_segment_t old_fs = force_uaccess_begin(); |
fc3562d79 maccess: move use... |
204 |
|
fc3562d79 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 uaccess: add forc... |
210 |
force_uaccess_end(old_fs); |
fc3562d79 maccess: move use... |
211 212 213 214 215 |
if (ret) return -EFAULT; return 0; } |
c0ee37e85 maccess: rename p... |
216 |
EXPORT_SYMBOL_GPL(copy_from_user_nofault); |
fc3562d79 maccess: move use... |
217 218 |
/** |
c0ee37e85 maccess: rename p... |
219 |
* copy_to_user_nofault(): safely attempt to write to a user-space location |
fc3562d79 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 maccess: rename p... |
227 |
long copy_to_user_nofault(void __user *dst, const void *src, size_t size) |
fc3562d79 maccess: move use... |
228 229 |
{ long ret = -EFAULT; |
3d13f313c uaccess: add forc... |
230 |
mm_segment_t old_fs = force_uaccess_begin(); |
fc3562d79 maccess: move use... |
231 |
|
fc3562d79 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 uaccess: add forc... |
237 |
force_uaccess_end(old_fs); |
fc3562d79 maccess: move use... |
238 239 240 241 242 |
if (ret) return -EFAULT; return 0; } |
c0ee37e85 maccess: rename p... |
243 |
EXPORT_SYMBOL_GPL(copy_to_user_nofault); |
fc3562d79 maccess: move use... |
244 245 |
/** |
bd88bb5d4 maccess: rename s... |
246 |
* strncpy_from_user_nofault: - Copy a NUL terminated string from unsafe user |
3d7081822 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 maccess: rename s... |
263 |
long strncpy_from_user_nofault(char *dst, const void __user *unsafe_addr, |
3d7081822 uaccess: Add non-... |
264 265 |
long count) { |
3d13f313c uaccess: add forc... |
266 |
mm_segment_t old_fs; |
3d7081822 uaccess: Add non-... |
267 268 269 270 |
long ret; if (unlikely(count <= 0)) return 0; |
3d13f313c uaccess: add forc... |
271 |
old_fs = force_uaccess_begin(); |
3d7081822 uaccess: Add non-... |
272 273 274 |
pagefault_disable(); ret = strncpy_from_user(dst, unsafe_addr, count); pagefault_enable(); |
3d13f313c uaccess: add forc... |
275 |
force_uaccess_end(old_fs); |
3d7081822 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 maccess: rename s... |
288 |
* strnlen_user_nofault: - Get the size of a user string INCLUDING final NUL. |
3d7081822 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 maccess: rename s... |
303 |
long strnlen_user_nofault(const void __user *unsafe_addr, long count) |
3d7081822 uaccess: Add non-... |
304 |
{ |
3d13f313c uaccess: add forc... |
305 |
mm_segment_t old_fs; |
3d7081822 uaccess: Add non-... |
306 |
int ret; |
3d13f313c uaccess: add forc... |
307 |
old_fs = force_uaccess_begin(); |
3d7081822 uaccess: Add non-... |
308 309 310 |
pagefault_disable(); ret = strnlen_user(unsafe_addr, count); pagefault_enable(); |
3d13f313c uaccess: add forc... |
311 |
force_uaccess_end(old_fs); |
3d7081822 uaccess: Add non-... |
312 313 314 |
return ret; } |