diff options
Diffstat (limited to 'mm')
-rw-r--r-- | mm/mremap.c | 120 |
1 files changed, 72 insertions, 48 deletions
diff --git a/mm/mremap.c b/mm/mremap.c index 67761361c469..5f346178f16f 100644 --- a/mm/mremap.c +++ b/mm/mremap.c | |||
@@ -313,6 +313,59 @@ Eagain: | |||
313 | return ERR_PTR(-EAGAIN); | 313 | return ERR_PTR(-EAGAIN); |
314 | } | 314 | } |
315 | 315 | ||
316 | static unsigned long mremap_to(unsigned long addr, | ||
317 | unsigned long old_len, unsigned long new_addr, | ||
318 | unsigned long new_len) | ||
319 | { | ||
320 | struct mm_struct *mm = current->mm; | ||
321 | struct vm_area_struct *vma; | ||
322 | unsigned long ret = -EINVAL; | ||
323 | unsigned long charged = 0; | ||
324 | |||
325 | if (new_addr & ~PAGE_MASK) | ||
326 | goto out; | ||
327 | |||
328 | if (new_len > TASK_SIZE || new_addr > TASK_SIZE - new_len) | ||
329 | goto out; | ||
330 | |||
331 | /* Check if the location we're moving into overlaps the | ||
332 | * old location at all, and fail if it does. | ||
333 | */ | ||
334 | if ((new_addr <= addr) && (new_addr+new_len) > addr) | ||
335 | goto out; | ||
336 | |||
337 | if ((addr <= new_addr) && (addr+old_len) > new_addr) | ||
338 | goto out; | ||
339 | |||
340 | ret = security_file_mmap(NULL, 0, 0, 0, new_addr, 1); | ||
341 | if (ret) | ||
342 | goto out; | ||
343 | |||
344 | ret = do_munmap(mm, new_addr, new_len); | ||
345 | if (ret) | ||
346 | goto out; | ||
347 | |||
348 | if (old_len >= new_len) { | ||
349 | ret = do_munmap(mm, addr+new_len, old_len - new_len); | ||
350 | if (ret && old_len != new_len) | ||
351 | goto out; | ||
352 | old_len = new_len; | ||
353 | } | ||
354 | |||
355 | vma = vma_to_resize(addr, old_len, new_len, &charged); | ||
356 | if (IS_ERR(vma)) { | ||
357 | ret = PTR_ERR(vma); | ||
358 | goto out; | ||
359 | } | ||
360 | |||
361 | ret = move_vma(vma, addr, old_len, new_len, new_addr); | ||
362 | if (ret & ~PAGE_MASK) | ||
363 | vm_unacct_memory(charged); | ||
364 | |||
365 | out: | ||
366 | return ret; | ||
367 | } | ||
368 | |||
316 | /* | 369 | /* |
317 | * Expand (or shrink) an existing mapping, potentially moving it at the | 370 | * Expand (or shrink) an existing mapping, potentially moving it at the |
318 | * same time (controlled by the MREMAP_MAYMOVE flag and available VM space) | 371 | * same time (controlled by the MREMAP_MAYMOVE flag and available VM space) |
@@ -346,32 +399,10 @@ unsigned long do_mremap(unsigned long addr, | |||
346 | if (!new_len) | 399 | if (!new_len) |
347 | goto out; | 400 | goto out; |
348 | 401 | ||
349 | /* new_addr is only valid if MREMAP_FIXED is specified */ | ||
350 | if (flags & MREMAP_FIXED) { | 402 | if (flags & MREMAP_FIXED) { |
351 | if (new_addr & ~PAGE_MASK) | 403 | if (flags & MREMAP_MAYMOVE) |
352 | goto out; | 404 | ret = mremap_to(addr, old_len, new_addr, new_len); |
353 | if (!(flags & MREMAP_MAYMOVE)) | 405 | goto out; |
354 | goto out; | ||
355 | |||
356 | if (new_len > TASK_SIZE || new_addr > TASK_SIZE - new_len) | ||
357 | goto out; | ||
358 | |||
359 | /* Check if the location we're moving into overlaps the | ||
360 | * old location at all, and fail if it does. | ||
361 | */ | ||
362 | if ((new_addr <= addr) && (new_addr+new_len) > addr) | ||
363 | goto out; | ||
364 | |||
365 | if ((addr <= new_addr) && (addr+old_len) > new_addr) | ||
366 | goto out; | ||
367 | |||
368 | ret = security_file_mmap(NULL, 0, 0, 0, new_addr, 1); | ||
369 | if (ret) | ||
370 | goto out; | ||
371 | |||
372 | ret = do_munmap(mm, new_addr, new_len); | ||
373 | if (ret) | ||
374 | goto out; | ||
375 | } | 406 | } |
376 | 407 | ||
377 | /* | 408 | /* |
@@ -384,13 +415,11 @@ unsigned long do_mremap(unsigned long addr, | |||
384 | if (ret && old_len != new_len) | 415 | if (ret && old_len != new_len) |
385 | goto out; | 416 | goto out; |
386 | ret = addr; | 417 | ret = addr; |
387 | if (!(flags & MREMAP_FIXED) || (new_addr == addr)) | 418 | goto out; |
388 | goto out; | ||
389 | old_len = new_len; | ||
390 | } | 419 | } |
391 | 420 | ||
392 | /* | 421 | /* |
393 | * Ok, we need to grow.. or relocate. | 422 | * Ok, we need to grow.. |
394 | */ | 423 | */ |
395 | vma = vma_to_resize(addr, old_len, new_len, &charged); | 424 | vma = vma_to_resize(addr, old_len, new_len, &charged); |
396 | if (IS_ERR(vma)) { | 425 | if (IS_ERR(vma)) { |
@@ -399,11 +428,8 @@ unsigned long do_mremap(unsigned long addr, | |||
399 | } | 428 | } |
400 | 429 | ||
401 | /* old_len exactly to the end of the area.. | 430 | /* old_len exactly to the end of the area.. |
402 | * And we're not relocating the area. | ||
403 | */ | 431 | */ |
404 | if (old_len == vma->vm_end - addr && | 432 | if (old_len == vma->vm_end - addr) { |
405 | !((flags & MREMAP_FIXED) && (addr != new_addr)) && | ||
406 | (old_len != new_len || !(flags & MREMAP_MAYMOVE))) { | ||
407 | unsigned long max_addr = TASK_SIZE; | 433 | unsigned long max_addr = TASK_SIZE; |
408 | if (vma->vm_next) | 434 | if (vma->vm_next) |
409 | max_addr = vma->vm_next->vm_start; | 435 | max_addr = vma->vm_next->vm_start; |
@@ -432,22 +458,20 @@ unsigned long do_mremap(unsigned long addr, | |||
432 | */ | 458 | */ |
433 | ret = -ENOMEM; | 459 | ret = -ENOMEM; |
434 | if (flags & MREMAP_MAYMOVE) { | 460 | if (flags & MREMAP_MAYMOVE) { |
435 | if (!(flags & MREMAP_FIXED)) { | 461 | unsigned long map_flags = 0; |
436 | unsigned long map_flags = 0; | 462 | if (vma->vm_flags & VM_MAYSHARE) |
437 | if (vma->vm_flags & VM_MAYSHARE) | 463 | map_flags |= MAP_SHARED; |
438 | map_flags |= MAP_SHARED; | 464 | |
439 | 465 | new_addr = get_unmapped_area(vma->vm_file, 0, new_len, | |
440 | new_addr = get_unmapped_area(vma->vm_file, 0, new_len, | 466 | vma->vm_pgoff, map_flags); |
441 | vma->vm_pgoff, map_flags); | 467 | if (new_addr & ~PAGE_MASK) { |
442 | if (new_addr & ~PAGE_MASK) { | 468 | ret = new_addr; |
443 | ret = new_addr; | 469 | goto out; |
444 | goto out; | ||
445 | } | ||
446 | |||
447 | ret = security_file_mmap(NULL, 0, 0, 0, new_addr, 1); | ||
448 | if (ret) | ||
449 | goto out; | ||
450 | } | 470 | } |
471 | |||
472 | ret = security_file_mmap(NULL, 0, 0, 0, new_addr, 1); | ||
473 | if (ret) | ||
474 | goto out; | ||
451 | ret = move_vma(vma, addr, old_len, new_len, new_addr); | 475 | ret = move_vma(vma, addr, old_len, new_len, new_addr); |
452 | } | 476 | } |
453 | out: | 477 | out: |