userfaultfd: shmem: avoid leaking blocks and used blocks in UFFDIO_COPY
authorAndrea Arcangeli <aarcange@redhat.com>
Wed, 22 Feb 2017 23:43:55 +0000 (15:43 -0800)
committerLinus Torvalds <torvalds@linux-foundation.org>
Thu, 23 Feb 2017 00:41:29 +0000 (16:41 -0800)
If the atomic copy_user fails because of a real dangling userland
pointer, we won't go back into the shmem method, so when the method
returns it must not leave anything charged up, except the page itself.

Link: http://lkml.kernel.org/r/20161216144821.5183-37-aarcange@redhat.com
Signed-off-by: Andrea Arcangeli <aarcange@redhat.com>
Cc: "Dr. David Alan Gilbert" <dgilbert@redhat.com>
Cc: Hillf Danton <hillf.zj@alibaba-inc.com>
Cc: Michael Rapoport <RAPOPORT@il.ibm.com>
Cc: Mike Kravetz <mike.kravetz@oracle.com>
Cc: Mike Rapoport <rppt@linux.vnet.ibm.com>
Cc: Pavel Emelyanov <xemul@parallels.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
mm/shmem.c

index 8d7d80cf8708bf00e0232a58f6beb99488d1623b..9c6d22ff44e26280505729463acaa96e8bf88d6a 100644 (file)
@@ -2214,17 +2214,17 @@ int shmem_mcopy_atomic_pte(struct mm_struct *dst_mm,
        pte_t _dst_pte, *dst_pte;
        int ret;
 
-       if (!*pagep) {
-               ret = -ENOMEM;
-               if (shmem_acct_block(info->flags, 1))
-                       goto out;
-               if (sbinfo->max_blocks) {
-                       if (percpu_counter_compare(&sbinfo->used_blocks,
-                                                  sbinfo->max_blocks) >= 0)
-                               goto out_unacct_blocks;
-                       percpu_counter_inc(&sbinfo->used_blocks);
-               }
+       ret = -ENOMEM;
+       if (shmem_acct_block(info->flags, 1))
+               goto out;
+       if (sbinfo->max_blocks) {
+               if (percpu_counter_compare(&sbinfo->used_blocks,
+                                          sbinfo->max_blocks) >= 0)
+                       goto out_unacct_blocks;
+               percpu_counter_inc(&sbinfo->used_blocks);
+       }
 
+       if (!*pagep) {
                page = shmem_alloc_page(gfp, info, pgoff);
                if (!page)
                        goto out_dec_used_blocks;
@@ -2237,6 +2237,9 @@ int shmem_mcopy_atomic_pte(struct mm_struct *dst_mm,
                /* fallback to copy_from_user outside mmap_sem */
                if (unlikely(ret)) {
                        *pagep = page;
+                       if (sbinfo->max_blocks)
+                               percpu_counter_add(&sbinfo->used_blocks, -1);
+                       shmem_unacct_blocks(info->flags, 1);
                        /* don't free the page */
                        return -EFAULT;
                }