aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--mm/mremap.c120
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
316static 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
365out:
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 }
453out: 477out: