diff options
Diffstat (limited to 'kernel/sys.c')
-rw-r--r-- | kernel/sys.c | 152 |
1 files changed, 152 insertions, 0 deletions
diff --git a/kernel/sys.c b/kernel/sys.c index a730c03ee607..e5b44b862dd2 100644 --- a/kernel/sys.c +++ b/kernel/sys.c | |||
@@ -42,6 +42,8 @@ | |||
42 | #include <linux/syscore_ops.h> | 42 | #include <linux/syscore_ops.h> |
43 | #include <linux/version.h> | 43 | #include <linux/version.h> |
44 | #include <linux/ctype.h> | 44 | #include <linux/ctype.h> |
45 | #include <linux/mm.h> | ||
46 | #include <linux/mempolicy.h> | ||
45 | 47 | ||
46 | #include <linux/compat.h> | 48 | #include <linux/compat.h> |
47 | #include <linux/syscalls.h> | 49 | #include <linux/syscalls.h> |
@@ -2278,6 +2280,153 @@ int __weak arch_prctl_spec_ctrl_set(struct task_struct *t, unsigned long which, | |||
2278 | return -EINVAL; | 2280 | return -EINVAL; |
2279 | } | 2281 | } |
2280 | 2282 | ||
2283 | #ifdef CONFIG_MMU | ||
2284 | static int prctl_update_vma_anon_name(struct vm_area_struct *vma, | ||
2285 | struct vm_area_struct **prev, | ||
2286 | unsigned long start, unsigned long end, | ||
2287 | const char __user *name_addr) | ||
2288 | { | ||
2289 | struct mm_struct *mm = vma->vm_mm; | ||
2290 | int error = 0; | ||
2291 | pgoff_t pgoff; | ||
2292 | |||
2293 | if (name_addr == vma_get_anon_name(vma)) { | ||
2294 | *prev = vma; | ||
2295 | goto out; | ||
2296 | } | ||
2297 | |||
2298 | pgoff = vma->vm_pgoff + ((start - vma->vm_start) >> PAGE_SHIFT); | ||
2299 | *prev = vma_merge(mm, *prev, start, end, vma->vm_flags, vma->anon_vma, | ||
2300 | vma->vm_file, pgoff, vma_policy(vma), | ||
2301 | vma->vm_userfaultfd_ctx, name_addr); | ||
2302 | if (*prev) { | ||
2303 | vma = *prev; | ||
2304 | goto success; | ||
2305 | } | ||
2306 | |||
2307 | *prev = vma; | ||
2308 | |||
2309 | if (start != vma->vm_start) { | ||
2310 | error = split_vma(mm, vma, start, 1); | ||
2311 | if (error) | ||
2312 | goto out; | ||
2313 | } | ||
2314 | |||
2315 | if (end != vma->vm_end) { | ||
2316 | error = split_vma(mm, vma, end, 0); | ||
2317 | if (error) | ||
2318 | goto out; | ||
2319 | } | ||
2320 | |||
2321 | success: | ||
2322 | if (!vma->vm_file) | ||
2323 | vma->anon_name = name_addr; | ||
2324 | |||
2325 | out: | ||
2326 | if (error == -ENOMEM) | ||
2327 | error = -EAGAIN; | ||
2328 | return error; | ||
2329 | } | ||
2330 | |||
2331 | static int prctl_set_vma_anon_name(unsigned long start, unsigned long end, | ||
2332 | unsigned long arg) | ||
2333 | { | ||
2334 | unsigned long tmp; | ||
2335 | struct vm_area_struct *vma, *prev; | ||
2336 | int unmapped_error = 0; | ||
2337 | int error = -EINVAL; | ||
2338 | |||
2339 | /* | ||
2340 | * If the interval [start,end) covers some unmapped address | ||
2341 | * ranges, just ignore them, but return -ENOMEM at the end. | ||
2342 | * - this matches the handling in madvise. | ||
2343 | */ | ||
2344 | vma = find_vma_prev(current->mm, start, &prev); | ||
2345 | if (vma && start > vma->vm_start) | ||
2346 | prev = vma; | ||
2347 | |||
2348 | for (;;) { | ||
2349 | /* Still start < end. */ | ||
2350 | error = -ENOMEM; | ||
2351 | if (!vma) | ||
2352 | return error; | ||
2353 | |||
2354 | /* Here start < (end|vma->vm_end). */ | ||
2355 | if (start < vma->vm_start) { | ||
2356 | unmapped_error = -ENOMEM; | ||
2357 | start = vma->vm_start; | ||
2358 | if (start >= end) | ||
2359 | return error; | ||
2360 | } | ||
2361 | |||
2362 | /* Here vma->vm_start <= start < (end|vma->vm_end) */ | ||
2363 | tmp = vma->vm_end; | ||
2364 | if (end < tmp) | ||
2365 | tmp = end; | ||
2366 | |||
2367 | /* Here vma->vm_start <= start < tmp <= (end|vma->vm_end). */ | ||
2368 | error = prctl_update_vma_anon_name(vma, &prev, start, tmp, | ||
2369 | (const char __user *)arg); | ||
2370 | if (error) | ||
2371 | return error; | ||
2372 | start = tmp; | ||
2373 | if (prev && start < prev->vm_end) | ||
2374 | start = prev->vm_end; | ||
2375 | error = unmapped_error; | ||
2376 | if (start >= end) | ||
2377 | return error; | ||
2378 | if (prev) | ||
2379 | vma = prev->vm_next; | ||
2380 | else /* madvise_remove dropped mmap_lock */ | ||
2381 | vma = find_vma(current->mm, start); | ||
2382 | } | ||
2383 | } | ||
2384 | |||
2385 | static int prctl_set_vma(unsigned long opt, unsigned long start, | ||
2386 | unsigned long len_in, unsigned long arg) | ||
2387 | { | ||
2388 | struct mm_struct *mm = current->mm; | ||
2389 | int error; | ||
2390 | unsigned long len; | ||
2391 | unsigned long end; | ||
2392 | |||
2393 | if (start & ~PAGE_MASK) | ||
2394 | return -EINVAL; | ||
2395 | len = (len_in + ~PAGE_MASK) & PAGE_MASK; | ||
2396 | |||
2397 | /* Check to see whether len was rounded up from small -ve to zero */ | ||
2398 | if (len_in && !len) | ||
2399 | return -EINVAL; | ||
2400 | |||
2401 | end = start + len; | ||
2402 | if (end < start) | ||
2403 | return -EINVAL; | ||
2404 | |||
2405 | if (end == start) | ||
2406 | return 0; | ||
2407 | |||
2408 | mmap_write_lock(mm); | ||
2409 | |||
2410 | switch (opt) { | ||
2411 | case PR_SET_VMA_ANON_NAME: | ||
2412 | error = prctl_set_vma_anon_name(start, end, arg); | ||
2413 | break; | ||
2414 | default: | ||
2415 | error = -EINVAL; | ||
2416 | } | ||
2417 | |||
2418 | mmap_write_unlock(mm); | ||
2419 | |||
2420 | return error; | ||
2421 | } | ||
2422 | #else /* CONFIG_MMU */ | ||
2423 | static int prctl_set_vma(unsigned long opt, unsigned long start, | ||
2424 | unsigned long len_in, unsigned long arg) | ||
2425 | { | ||
2426 | return -EINVAL; | ||
2427 | } | ||
2428 | #endif | ||
2429 | |||
2281 | #define PR_IO_FLUSHER (PF_MEMALLOC_NOIO | PF_LOCAL_THROTTLE) | 2430 | #define PR_IO_FLUSHER (PF_MEMALLOC_NOIO | PF_LOCAL_THROTTLE) |
2282 | 2431 | ||
2283 | SYSCALL_DEFINE5(prctl, int, option, unsigned long, arg2, unsigned long, arg3, | 2432 | SYSCALL_DEFINE5(prctl, int, option, unsigned long, arg2, unsigned long, arg3, |
@@ -2492,6 +2641,9 @@ SYSCALL_DEFINE5(prctl, int, option, unsigned long, arg2, unsigned long, arg3, | |||
2492 | return -EINVAL; | 2641 | return -EINVAL; |
2493 | error = arch_prctl_spec_ctrl_set(me, arg2, arg3); | 2642 | error = arch_prctl_spec_ctrl_set(me, arg2, arg3); |
2494 | break; | 2643 | break; |
2644 | case PR_SET_VMA: | ||
2645 | error = prctl_set_vma(arg2, arg3, arg4, arg5); | ||
2646 | break; | ||
2495 | case PR_PAC_RESET_KEYS: | 2647 | case PR_PAC_RESET_KEYS: |
2496 | if (arg3 || arg4 || arg5) | 2648 | if (arg3 || arg4 || arg5) |
2497 | return -EINVAL; | 2649 | return -EINVAL; |