diff options
author | Oleg Nesterov <oleg@redhat.com> | 2019-08-24 20:54:56 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2019-08-24 22:48:42 -0400 |
commit | 46d0b24c5ee10a15dfb25e20642f5a5ed59c5003 (patch) | |
tree | 6467a794faee3eac2a2839f70478f3fa22dfdd9c | |
parent | 7b2b55da1db10a5525460633ae4b6fb0be060c41 (diff) |
userfaultfd_release: always remove uffd flags and clear vm_userfaultfd_ctx
userfaultfd_release() should clear vm_flags/vm_userfaultfd_ctx even if
mm->core_state != NULL.
Otherwise a page fault can see userfaultfd_missing() == T and use an
already freed userfaultfd_ctx.
Link: http://lkml.kernel.org/r/20190820160237.GB4983@redhat.com
Fixes: 04f5866e41fb ("coredump: fix race condition between mmget_not_zero()/get_task_mm() and core dumping")
Signed-off-by: Oleg Nesterov <oleg@redhat.com>
Reported-by: Kefeng Wang <wangkefeng.wang@huawei.com>
Reviewed-by: Andrea Arcangeli <aarcange@redhat.com>
Tested-by: Kefeng Wang <wangkefeng.wang@huawei.com>
Cc: Peter Xu <peterx@redhat.com>
Cc: Mike Rapoport <rppt@linux.ibm.com>
Cc: Jann Horn <jannh@google.com>
Cc: Jason Gunthorpe <jgg@mellanox.com>
Cc: Michal Hocko <mhocko@suse.com>
Cc: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp>
Cc: <stable@vger.kernel.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
-rw-r--r-- | fs/userfaultfd.c | 25 |
1 files changed, 13 insertions, 12 deletions
diff --git a/fs/userfaultfd.c b/fs/userfaultfd.c index ccbdbd62f0d8..fe6d804a38dc 100644 --- a/fs/userfaultfd.c +++ b/fs/userfaultfd.c | |||
@@ -880,6 +880,7 @@ static int userfaultfd_release(struct inode *inode, struct file *file) | |||
880 | /* len == 0 means wake all */ | 880 | /* len == 0 means wake all */ |
881 | struct userfaultfd_wake_range range = { .len = 0, }; | 881 | struct userfaultfd_wake_range range = { .len = 0, }; |
882 | unsigned long new_flags; | 882 | unsigned long new_flags; |
883 | bool still_valid; | ||
883 | 884 | ||
884 | WRITE_ONCE(ctx->released, true); | 885 | WRITE_ONCE(ctx->released, true); |
885 | 886 | ||
@@ -895,8 +896,7 @@ static int userfaultfd_release(struct inode *inode, struct file *file) | |||
895 | * taking the mmap_sem for writing. | 896 | * taking the mmap_sem for writing. |
896 | */ | 897 | */ |
897 | down_write(&mm->mmap_sem); | 898 | down_write(&mm->mmap_sem); |
898 | if (!mmget_still_valid(mm)) | 899 | still_valid = mmget_still_valid(mm); |
899 | goto skip_mm; | ||
900 | prev = NULL; | 900 | prev = NULL; |
901 | for (vma = mm->mmap; vma; vma = vma->vm_next) { | 901 | for (vma = mm->mmap; vma; vma = vma->vm_next) { |
902 | cond_resched(); | 902 | cond_resched(); |
@@ -907,19 +907,20 @@ static int userfaultfd_release(struct inode *inode, struct file *file) | |||
907 | continue; | 907 | continue; |
908 | } | 908 | } |
909 | new_flags = vma->vm_flags & ~(VM_UFFD_MISSING | VM_UFFD_WP); | 909 | new_flags = vma->vm_flags & ~(VM_UFFD_MISSING | VM_UFFD_WP); |
910 | prev = vma_merge(mm, prev, vma->vm_start, vma->vm_end, | 910 | if (still_valid) { |
911 | new_flags, vma->anon_vma, | 911 | prev = vma_merge(mm, prev, vma->vm_start, vma->vm_end, |
912 | vma->vm_file, vma->vm_pgoff, | 912 | new_flags, vma->anon_vma, |
913 | vma_policy(vma), | 913 | vma->vm_file, vma->vm_pgoff, |
914 | NULL_VM_UFFD_CTX); | 914 | vma_policy(vma), |
915 | if (prev) | 915 | NULL_VM_UFFD_CTX); |
916 | vma = prev; | 916 | if (prev) |
917 | else | 917 | vma = prev; |
918 | prev = vma; | 918 | else |
919 | prev = vma; | ||
920 | } | ||
919 | vma->vm_flags = new_flags; | 921 | vma->vm_flags = new_flags; |
920 | vma->vm_userfaultfd_ctx = NULL_VM_UFFD_CTX; | 922 | vma->vm_userfaultfd_ctx = NULL_VM_UFFD_CTX; |
921 | } | 923 | } |
922 | skip_mm: | ||
923 | up_write(&mm->mmap_sem); | 924 | up_write(&mm->mmap_sem); |
924 | mmput(mm); | 925 | mmput(mm); |
925 | wakeup: | 926 | wakeup: |