Commit ecc1a899 authored by Al Viro's avatar Al Viro

do_mremap() untangling, part 2

Take the MREMAP_FIXED into a separate helper, simplify the living
hell out of conditions in both cases.
Acked-by: default avatarRussell King <rmk+kernel@arm.linux.org.uk>
Acked-by: default avatarHugh Dickins <hugh.dickins@tiscali.co.uk>
Signed-off-by: default avatarAl Viro <viro@zeniv.linux.org.uk>
parent 54f5de70
...@@ -313,6 +313,59 @@ static struct vm_area_struct *vma_to_resize(unsigned long addr, ...@@ -313,6 +313,59 @@ static struct vm_area_struct *vma_to_resize(unsigned long addr,
return ERR_PTR(-EAGAIN); return ERR_PTR(-EAGAIN);
} }
static unsigned long mremap_to(unsigned long addr,
unsigned long old_len, unsigned long new_addr,
unsigned long new_len)
{
struct mm_struct *mm = current->mm;
struct vm_area_struct *vma;
unsigned long ret = -EINVAL;
unsigned long charged = 0;
if (new_addr & ~PAGE_MASK)
goto out;
if (new_len > TASK_SIZE || new_addr > TASK_SIZE - new_len)
goto out;
/* Check if the location we're moving into overlaps the
* old location at all, and fail if it does.
*/
if ((new_addr <= addr) && (new_addr+new_len) > addr)
goto out;
if ((addr <= new_addr) && (addr+old_len) > new_addr)
goto out;
ret = security_file_mmap(NULL, 0, 0, 0, new_addr, 1);
if (ret)
goto out;
ret = do_munmap(mm, new_addr, new_len);
if (ret)
goto out;
if (old_len >= new_len) {
ret = do_munmap(mm, addr+new_len, old_len - new_len);
if (ret && old_len != new_len)
goto out;
old_len = new_len;
}
vma = vma_to_resize(addr, old_len, new_len, &charged);
if (IS_ERR(vma)) {
ret = PTR_ERR(vma);
goto out;
}
ret = move_vma(vma, addr, old_len, new_len, new_addr);
if (ret & ~PAGE_MASK)
vm_unacct_memory(charged);
out:
return ret;
}
/* /*
* Expand (or shrink) an existing mapping, potentially moving it at the * Expand (or shrink) an existing mapping, potentially moving it at the
* same time (controlled by the MREMAP_MAYMOVE flag and available VM space) * same time (controlled by the MREMAP_MAYMOVE flag and available VM space)
...@@ -346,32 +399,10 @@ unsigned long do_mremap(unsigned long addr, ...@@ -346,32 +399,10 @@ unsigned long do_mremap(unsigned long addr,
if (!new_len) if (!new_len)
goto out; goto out;
/* new_addr is only valid if MREMAP_FIXED is specified */
if (flags & MREMAP_FIXED) { if (flags & MREMAP_FIXED) {
if (new_addr & ~PAGE_MASK) if (flags & MREMAP_MAYMOVE)
goto out; ret = mremap_to(addr, old_len, new_addr, new_len);
if (!(flags & MREMAP_MAYMOVE)) goto out;
goto out;
if (new_len > TASK_SIZE || new_addr > TASK_SIZE - new_len)
goto out;
/* Check if the location we're moving into overlaps the
* old location at all, and fail if it does.
*/
if ((new_addr <= addr) && (new_addr+new_len) > addr)
goto out;
if ((addr <= new_addr) && (addr+old_len) > new_addr)
goto out;
ret = security_file_mmap(NULL, 0, 0, 0, new_addr, 1);
if (ret)
goto out;
ret = do_munmap(mm, new_addr, new_len);
if (ret)
goto out;
} }
/* /*
...@@ -384,13 +415,11 @@ unsigned long do_mremap(unsigned long addr, ...@@ -384,13 +415,11 @@ unsigned long do_mremap(unsigned long addr,
if (ret && old_len != new_len) if (ret && old_len != new_len)
goto out; goto out;
ret = addr; ret = addr;
if (!(flags & MREMAP_FIXED) || (new_addr == addr)) goto out;
goto out;
old_len = new_len;
} }
/* /*
* Ok, we need to grow.. or relocate. * Ok, we need to grow..
*/ */
vma = vma_to_resize(addr, old_len, new_len, &charged); vma = vma_to_resize(addr, old_len, new_len, &charged);
if (IS_ERR(vma)) { if (IS_ERR(vma)) {
...@@ -399,11 +428,8 @@ unsigned long do_mremap(unsigned long addr, ...@@ -399,11 +428,8 @@ unsigned long do_mremap(unsigned long addr,
} }
/* old_len exactly to the end of the area.. /* old_len exactly to the end of the area..
* And we're not relocating the area.
*/ */
if (old_len == vma->vm_end - addr && if (old_len == vma->vm_end - addr) {
!((flags & MREMAP_FIXED) && (addr != new_addr)) &&
(old_len != new_len || !(flags & MREMAP_MAYMOVE))) {
unsigned long max_addr = TASK_SIZE; unsigned long max_addr = TASK_SIZE;
if (vma->vm_next) if (vma->vm_next)
max_addr = vma->vm_next->vm_start; max_addr = vma->vm_next->vm_start;
...@@ -432,22 +458,20 @@ unsigned long do_mremap(unsigned long addr, ...@@ -432,22 +458,20 @@ unsigned long do_mremap(unsigned long addr,
*/ */
ret = -ENOMEM; ret = -ENOMEM;
if (flags & MREMAP_MAYMOVE) { if (flags & MREMAP_MAYMOVE) {
if (!(flags & MREMAP_FIXED)) { unsigned long map_flags = 0;
unsigned long map_flags = 0; if (vma->vm_flags & VM_MAYSHARE)
if (vma->vm_flags & VM_MAYSHARE) map_flags |= MAP_SHARED;
map_flags |= MAP_SHARED;
new_addr = get_unmapped_area(vma->vm_file, 0, new_len,
new_addr = get_unmapped_area(vma->vm_file, 0, new_len, vma->vm_pgoff, map_flags);
vma->vm_pgoff, map_flags); if (new_addr & ~PAGE_MASK) {
if (new_addr & ~PAGE_MASK) { ret = new_addr;
ret = new_addr; goto out;
goto out;
}
ret = security_file_mmap(NULL, 0, 0, 0, new_addr, 1);
if (ret)
goto out;
} }
ret = security_file_mmap(NULL, 0, 0, 0, new_addr, 1);
if (ret)
goto out;
ret = move_vma(vma, addr, old_len, new_len, new_addr); ret = move_vma(vma, addr, old_len, new_len, new_addr);
} }
out: out:
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment