]> www.infradead.org Git - users/willy/linux.git/commitdiff
start of reparenting VMAs. buggy. mshare
authorMatthew Wilcox (Oracle) <willy@infradead.org>
Wed, 30 Sep 2020 16:49:02 +0000 (12:49 -0400)
committerMatthew Wilcox (Oracle) <willy@infradead.org>
Wed, 30 Sep 2020 16:49:02 +0000 (12:49 -0400)
mm/mmap.c
mm/mshare.c

index 8c7ca737a19b3db6274c9f918c00bfdfe9597dcf..376837e90b4b25a57919fe20b1a58386377c55a8 100644 (file)
--- a/mm/mmap.c
+++ b/mm/mmap.c
@@ -482,8 +482,7 @@ static __always_inline void vma_rb_erase_ignore(struct vm_area_struct *vma,
        __vma_rb_erase(vma, root);
 }
 
-static __always_inline void vma_rb_erase(struct vm_area_struct *vma,
-                                        struct rb_root *root)
+void vma_rb_erase(struct vm_area_struct *vma, struct rb_root *root)
 {
        /*
         * All rb_subtree_gap values must be consistent prior to erase,
index f79e6519c94a9b85663dad478b7284b5d06253c9..618912b1d14e6e15ad34c9e2ad99c195dc8a0596 100644 (file)
@@ -71,13 +71,65 @@ static const struct file_operations mshare_fops = {
        .release = mshare_release,
 };
 
+void vma_rb_erase(struct vm_area_struct *vma, struct rb_root *root);
+
+static
+bool move_vmas(struct mm_struct *new_mm, unsigned long addr, unsigned long len)
+{
+       struct mm_struct *old_mm = current->mm;
+       struct vm_area_struct *vma, *next, *prev = NULL;
+       bool success = false;
+       int i = 0;
+
+       mmap_write_lock(old_mm);
+
+       /* Must not overlap end */
+       vma = find_vma(old_mm, addr + len);
+       if (vma && vma->vm_start < addr + len)
+               goto unlock;
+       vma = find_vma(old_mm, addr);
+       if (vma && vma->vm_start < addr)
+               goto unlock;
+       success = true;
+
+       if (vma)
+               prev = vma->vm_prev;
+       while (vma && vma->vm_start < addr + len) {
+               int ret;
+
+printk("found vma start %lx\n", vma->vm_start);
+               vma_rb_erase(vma, &old_mm->mm_rb);
+               old_mm->map_count--;
+
+               next = vma->vm_next;
+               ret = insert_vm_struct(new_mm, vma);
+               if (ret)
+                       goto unlock;
+               /* XXX: VM_ACCOUNT? */
+               vm_stat_account(new_mm, vma->vm_flags, -vma_pages(vma));
+               vma = next;
+       }
+       if (prev)
+               prev->vm_next = vma;
+       if (vma)
+               vma->vm_prev = prev;
+
+       while (addr < new_mm->task_size) {
+               new_mm->pgd[i++] = *pgd_offset(old_mm, addr);
+               addr += PGDIR_SIZE;
+       }
+unlock:
+       mmap_write_unlock(old_mm);
+
+       return success;
+}
+
 SYSCALL_DEFINE3(mshare, unsigned long, addr, unsigned long, len,
                unsigned long, flags)
 {
        struct mm_struct *mm;
-       struct vm_area_struct *vma;
+       struct file *file;
        int fd;
-       int i = 0;
 
        if ((addr | len) & (PGDIR_SIZE - 1))
                return -EINVAL;
@@ -92,30 +144,24 @@ SYSCALL_DEFINE3(mshare, unsigned long, addr, unsigned long, len,
        if (!mm->task_size)
                mm->task_size--;
 
-       mmap_write_lock(current->mm);
-
-       vma = find_vma(current->mm, addr + len);
-       if (vma && vma->vm_start < addr + len)
-               goto unlock;
-       vma = find_vma(current->mm, addr);
-       if (vma && vma->vm_start < addr)
-               goto unlock;
-
-       while (addr < mm->task_size) {
-               mm->pgd[i++] = *pgd_offset(current->mm, addr);
-               addr += PGDIR_SIZE;
-       }
-       mmap_write_unlock(current->mm);
-
-       fd = anon_inode_getfd("mshare", &mshare_fops, mm, O_RDWR);
+       fd = get_unused_fd_flags(O_RDWR);
        if (fd < 0)
-               goto nofd;
+               goto out;
+       file = anon_inode_getfile("mshare", &mshare_fops, mm, O_RDWR);
+       if (IS_ERR(file))
+               goto nofile;
+
+       if (!move_vmas(mm, addr, len))
+               goto invalid;
+       fd_install(fd, file);
 out:
        return fd;
-unlock:
-       mmap_write_unlock(current->mm);
-       fd = -EINVAL;
-nofd:
+invalid:
+       fput(file);
+       file = ERR_PTR(-EINVAL);
+nofile:
+       put_unused_fd(fd);
+       fd = PTR_ERR(file);
        mmput(mm);
        goto out;
 }