diff options
author | Andrea Arcangeli <aarcange@redhat.com> | 2017-02-23 00:43:55 +0100 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2017-02-23 01:41:29 +0100 |
commit | cb658a453b9327ce96ce5222c24d162b5b65b564 (patch) | |
tree | dc5078cdec1b41f2ff11e0ee693564a9a47e806e /mm/shmem.c | |
parent | userfaultfd: shmem: avoid a lockup resulting from corrupted page->flags (diff) | |
download | linux-cb658a453b9327ce96ce5222c24d162b5b65b564.tar.xz linux-cb658a453b9327ce96ce5222c24d162b5b65b564.zip |
userfaultfd: shmem: avoid leaking blocks and used blocks in UFFDIO_COPY
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>
Diffstat (limited to 'mm/shmem.c')
-rw-r--r-- | mm/shmem.c | 23 |
1 files changed, 13 insertions, 10 deletions
diff --git a/mm/shmem.c b/mm/shmem.c index 8d7d80cf8708..9c6d22ff44e2 100644 --- a/mm/shmem.c +++ b/mm/shmem.c @@ -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; } |