Test-case:

        #define _GNU_SOURCE
        #include <stdio.h>
        #include <unistd.h>
        #include <stdlib.h>
        #include <string.h>
        #include <sys/mman.h>
        #include <assert.h>

        void *find_vdso_vaddr(void)
        {
                FILE *perl;
                char buf[32] = {};

                perl = popen("perl -e 'open STDIN,qq|/proc/@{[getppid]}/maps|;"
                                "/^(.*?)-.*vdso/ && print hex $1 while <>'", 
"r");
                fread(buf, sizeof(buf), 1, perl);
                fclose(perl);

                return (void *)atol(buf);
        }

        #define PAGE_SIZE       4096

        void *get_unmapped_area(void)
        {
                void *p = mmap(0, PAGE_SIZE, PROT_NONE,
                                MAP_PRIVATE|MAP_ANONYMOUS, -1,0);
                assert(p != MAP_FAILED);
                munmap(p, PAGE_SIZE);
                return p;
        }

        char save[2][PAGE_SIZE];

        int main(void)
        {
                void *vdso = find_vdso_vaddr();
                void *page[2];

                assert(vdso);
                memcpy(save, vdso, sizeof (save));
                // force another fault on the next check
                assert(madvise(vdso, 2 * PAGE_SIZE, MADV_DONTNEED) == 0);

                page[0] = mremap(vdso,
                                PAGE_SIZE, PAGE_SIZE, MREMAP_FIXED | 
MREMAP_MAYMOVE,
                                get_unmapped_area());
                page[1] = mremap(vdso + PAGE_SIZE,
                                PAGE_SIZE, PAGE_SIZE, MREMAP_FIXED | 
MREMAP_MAYMOVE,
                                get_unmapped_area());

                assert(page[0] != MAP_FAILED && page[1] != MAP_FAILED);
                printf("match: %d %d\n",
                        !memcmp(save[0], page[0], PAGE_SIZE),
                        !memcmp(save[1], page[1], PAGE_SIZE));

                return 0;
        }

fails without this patch. Before the previous commit it gets the wrong
page, now it segfaults (which is imho better).

This is because copy_vma() wrongly assumes that if vma->vm_file == NULL
is irrelevant until the first fault which will use do_anonymous_page().
This is obviously wrong for the special mapping.

Signed-off-by: Oleg Nesterov <o...@redhat.com>
---
 mm/mmap.c |    2 +-
 1 files changed, 1 insertions(+), 1 deletions(-)

diff --git a/mm/mmap.c b/mm/mmap.c
index 992417f..2185cd9 100644
--- a/mm/mmap.c
+++ b/mm/mmap.c
@@ -2905,7 +2905,7 @@ struct vm_area_struct *copy_vma(struct vm_area_struct 
**vmap,
         * If anonymous vma has not yet been faulted, update new pgoff
         * to match new location, to increase its chance of merging.
         */
-       if (unlikely(!vma->vm_file && !vma->anon_vma)) {
+       if (unlikely(vma_is_anonymous(vma) && !vma->anon_vma)) {
                pgoff = addr >> PAGE_SHIFT;
                faulted_in_anon_vma = false;
        }
-- 
1.5.5.1

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
Please read the FAQ at  http://www.tux.org/lkml/

Reply via email to