lkml.org 
[lkml]   [2018]   [Sep]   [30]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    From
    Date
    Subject[PATCH 3.16 053/131] mm: fix regression in remap_file_pages() emulation
    3.16.59-rc1 review patch.  If anyone has any objections, please let me know.

    ------------------

    From: "Kirill A. Shutemov" <kirill.shutemov@linux.intel.com>

    commit 48f7df329474b49d83d0dffec1b6186647f11976 upstream.

    Grazvydas Ignotas has reported a regression in remap_file_pages()
    emulation.

    Testcase:
    #define _GNU_SOURCE
    #include <assert.h>
    #include <stdlib.h>
    #include <stdio.h>
    #include <sys/mman.h>

    #define SIZE (4096 * 3)

    int main(int argc, char **argv)
    {
    unsigned long *p;
    long i;

    p = mmap(NULL, SIZE, PROT_READ | PROT_WRITE,
    MAP_SHARED | MAP_ANONYMOUS, -1, 0);
    if (p == MAP_FAILED) {
    perror("mmap");
    return -1;
    }

    for (i = 0; i < SIZE / 4096; i++)
    p[i * 4096 / sizeof(*p)] = i;

    if (remap_file_pages(p, 4096, 0, 1, 0)) {
    perror("remap_file_pages");
    return -1;
    }

    if (remap_file_pages(p, 4096 * 2, 0, 1, 0)) {
    perror("remap_file_pages");
    return -1;
    }

    assert(p[0] == 1);

    munmap(p, SIZE);

    return 0;
    }

    The second remap_file_pages() fails with -EINVAL.

    The reason is that remap_file_pages() emulation assumes that the target
    vma covers whole area we want to over map. That assumption is broken by
    first remap_file_pages() call: it split the area into two vma.

    The solution is to check next adjacent vmas, if they map the same file
    with the same flags.

    Fixes: c8d78c1823f4 ("mm: replace remap_file_pages() syscall with emulation")
    Signed-off-by: Kirill A. Shutemov <kirill.shutemov@linux.intel.com>
    Reported-by: Grazvydas Ignotas <notasas@gmail.com>
    Tested-by: Grazvydas Ignotas <notasas@gmail.com>
    Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
    Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
    Signed-off-by: Ben Hutchings <ben@decadent.org.uk>
    ---
    mm/mmap.c | 34 +++++++++++++++++++++++++++++-----
    1 file changed, 29 insertions(+), 5 deletions(-)

    --- a/mm/mmap.c
    +++ b/mm/mmap.c
    @@ -2656,12 +2656,29 @@ SYSCALL_DEFINE5(remap_file_pages, unsign
    if (!vma || !(vma->vm_flags & VM_SHARED))
    goto out;

    - if (start < vma->vm_start || start + size > vma->vm_end)
    + if (start < vma->vm_start)
    goto out;

    - if (pgoff == linear_page_index(vma, start)) {
    - ret = 0;
    - goto out;
    + if (start + size > vma->vm_end) {
    + struct vm_area_struct *next;
    +
    + for (next = vma->vm_next; next; next = next->vm_next) {
    + /* hole between vmas ? */
    + if (next->vm_start != next->vm_prev->vm_end)
    + goto out;
    +
    + if (next->vm_file != vma->vm_file)
    + goto out;
    +
    + if (next->vm_flags != vma->vm_flags)
    + goto out;
    +
    + if (start + size <= next->vm_end)
    + break;
    + }
    +
    + if (!next)
    + goto out;
    }

    prot |= vma->vm_flags & VM_READ ? PROT_READ : 0;
    @@ -2671,9 +2688,16 @@ SYSCALL_DEFINE5(remap_file_pages, unsign
    flags &= MAP_NONBLOCK;
    flags |= MAP_SHARED | MAP_FIXED | MAP_POPULATE;
    if (vma->vm_flags & VM_LOCKED) {
    + struct vm_area_struct *tmp;
    flags |= MAP_LOCKED;
    +
    /* drop PG_Mlocked flag for over-mapped range */
    - munlock_vma_pages_range(vma, start, start + size);
    + for (tmp = vma; tmp->vm_start >= start + size;
    + tmp = tmp->vm_next) {
    + munlock_vma_pages_range(tmp,
    + max(tmp->vm_start, start),
    + min(tmp->vm_end, start + size));
    + }
    }

    file = get_file(vma->vm_file);
    \
     
     \ /
      Last update: 2018-09-29 23:50    [W:4.224 / U:0.252 seconds]
    ©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site