Merge 4.14.105 into android-4.14-p
[GitHub/LineageOS/android_kernel_motorola_exynos9610.git] / mm / mmap.c
index 389c929424f5dde9330e9731a4faa6a737d48960..a05f201917e255f2b3fe9c31a8d9a243b24340ac 100644 (file)
--- a/mm/mmap.c
+++ b/mm/mmap.c
@@ -177,8 +177,8 @@ static struct vm_area_struct *remove_vma(struct vm_area_struct *vma)
        return next;
 }
 
-static int do_brk(unsigned long addr, unsigned long len, struct list_head *uf);
-
+static int do_brk_flags(unsigned long addr, unsigned long request, unsigned long flags,
+               struct list_head *uf);
 SYSCALL_DEFINE1(brk, unsigned long, brk)
 {
        unsigned long retval;
@@ -236,7 +236,7 @@ SYSCALL_DEFINE1(brk, unsigned long, brk)
                goto out;
 
        /* Ok, looks good - let it rip. */
-       if (do_brk(oldbrk, newbrk-oldbrk, &uf) < 0)
+       if (do_brk_flags(oldbrk, newbrk-oldbrk, 0, &uf) < 0)
                goto out;
 
 set_brk:
@@ -1324,6 +1324,35 @@ static inline int mlock_future_check(struct mm_struct *mm,
        return 0;
 }
 
+static inline u64 file_mmap_size_max(struct file *file, struct inode *inode)
+{
+       if (S_ISREG(inode->i_mode))
+               return MAX_LFS_FILESIZE;
+
+       if (S_ISBLK(inode->i_mode))
+               return MAX_LFS_FILESIZE;
+
+       /* Special "we do even unsigned file positions" case */
+       if (file->f_mode & FMODE_UNSIGNED_OFFSET)
+               return 0;
+
+       /* Yes, random drivers might want more. But I'm tired of buggy drivers */
+       return ULONG_MAX;
+}
+
+static inline bool file_mmap_ok(struct file *file, struct inode *inode,
+                               unsigned long pgoff, unsigned long len)
+{
+       u64 maxsize = file_mmap_size_max(file, inode);
+
+       if (maxsize && len > maxsize)
+               return false;
+       maxsize -= len;
+       if (pgoff > maxsize >> PAGE_SHIFT)
+               return false;
+       return true;
+}
+
 /*
  * The caller must hold down_write(&current->mm->mmap_sem).
  */
@@ -1397,6 +1426,9 @@ unsigned long do_mmap(struct file *file, unsigned long addr,
        if (file) {
                struct inode *inode = file_inode(file);
 
+               if (!file_mmap_ok(file, inode, pgoff, len))
+                       return -EOVERFLOW;
+
                switch (flags & MAP_TYPE) {
                case MAP_SHARED:
                        if ((prot&PROT_WRITE) && !(file->f_mode&FMODE_WRITE))
@@ -2325,12 +2357,11 @@ int expand_downwards(struct vm_area_struct *vma,
 {
        struct mm_struct *mm = vma->vm_mm;
        struct vm_area_struct *prev;
-       int error;
+       int error = 0;
 
        address &= PAGE_MASK;
-       error = security_mmap_addr(address);
-       if (error)
-               return error;
+       if (address < mmap_min_addr)
+               return -EPERM;
 
        /* Enforce stack_guard_gap */
        prev = vma->vm_prev;
@@ -2865,21 +2896,14 @@ static inline void verify_mm_writelocked(struct mm_struct *mm)
  *  anonymous maps.  eventually we may be able to do some
  *  brk-specific accounting here.
  */
-static int do_brk_flags(unsigned long addr, unsigned long request, unsigned long flags, struct list_head *uf)
+static int do_brk_flags(unsigned long addr, unsigned long len, unsigned long flags, struct list_head *uf)
 {
        struct mm_struct *mm = current->mm;
        struct vm_area_struct *vma, *prev;
-       unsigned long len;
        struct rb_node **rb_link, *rb_parent;
        pgoff_t pgoff = addr >> PAGE_SHIFT;
        int error;
 
-       len = PAGE_ALIGN(request);
-       if (len < request)
-               return -ENOMEM;
-       if (!len)
-               return 0;
-
        /* Until we need other flags, refuse anything except VM_EXEC. */
        if ((flags & (~VM_EXEC)) != 0)
                return -EINVAL;
@@ -2951,18 +2975,20 @@ out:
        return 0;
 }
 
-static int do_brk(unsigned long addr, unsigned long len, struct list_head *uf)
-{
-       return do_brk_flags(addr, len, 0, uf);
-}
-
-int vm_brk_flags(unsigned long addr, unsigned long len, unsigned long flags)
+int vm_brk_flags(unsigned long addr, unsigned long request, unsigned long flags)
 {
        struct mm_struct *mm = current->mm;
+       unsigned long len;
        int ret;
        bool populate;
        LIST_HEAD(uf);
 
+       len = PAGE_ALIGN(request);
+       if (len < request)
+               return -ENOMEM;
+       if (!len)
+               return 0;
+
        if (down_write_killable(&mm->mmap_sem))
                return -EINTR;
 
@@ -2992,6 +3018,32 @@ void exit_mmap(struct mm_struct *mm)
        /* mm's last user has gone, and its about to be pulled down */
        mmu_notifier_release(mm);
 
+       if (unlikely(mm_is_oom_victim(mm))) {
+               /*
+                * Manually reap the mm to free as much memory as possible.
+                * Then, as the oom reaper does, set MMF_OOM_SKIP to disregard
+                * this mm from further consideration.  Taking mm->mmap_sem for
+                * write after setting MMF_OOM_SKIP will guarantee that the oom
+                * reaper will not run on this mm again after mmap_sem is
+                * dropped.
+                *
+                * Nothing can be holding mm->mmap_sem here and the above call
+                * to mmu_notifier_release(mm) ensures mmu notifier callbacks in
+                * __oom_reap_task_mm() will not block.
+                *
+                * This needs to be done before calling munlock_vma_pages_all(),
+                * which clears VM_LOCKED, otherwise the oom reaper cannot
+                * reliably test it.
+                */
+               mutex_lock(&oom_lock);
+               __oom_reap_task_mm(mm);
+               mutex_unlock(&oom_lock);
+
+               set_bit(MMF_OOM_SKIP, &mm->flags);
+               down_write(&mm->mmap_sem);
+               up_write(&mm->mmap_sem);
+       }
+
        if (mm->locked_vm) {
                vma = mm->mmap;
                while (vma) {
@@ -3013,24 +3065,6 @@ void exit_mmap(struct mm_struct *mm)
        /* update_hiwater_rss(mm) here? but nobody should be looking */
        /* Use -1 here to ensure all VMAs in the mm are unmapped */
        unmap_vmas(&tlb, vma, 0, -1);
-
-       if (unlikely(mm_is_oom_victim(mm))) {
-               /*
-                * Wait for oom_reap_task() to stop working on this
-                * mm. Because MMF_OOM_SKIP is already set before
-                * calling down_read(), oom_reap_task() will not run
-                * on this "mm" post up_write().
-                *
-                * mm_is_oom_victim() cannot be set from under us
-                * either because victim->mm is already set to NULL
-                * under task_lock before calling mmput and oom_mm is
-                * set not NULL by the OOM killer only if victim->mm
-                * is found not NULL while holding the task_lock.
-                */
-               set_bit(MMF_OOM_SKIP, &mm->flags);
-               down_write(&mm->mmap_sem);
-               up_write(&mm->mmap_sem);
-       }
        free_pgtables(&tlb, vma, FIRST_USER_ADDRESS, USER_PGTABLES_CEILING);
        tlb_finish_mmu(&tlb, 0, -1);