aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorKirill A. Shutemov <kirill.shutemov@linux.intel.com>2016-09-19 17:44:01 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2016-09-19 18:36:16 -0400
commitc131f751ab1a852d4dd4b490b3a7fbba7d738de5 (patch)
tree8aad30641286414906d71e5bcfa7f8274998a601
parentd8e3875431956c1f78e142d531f490f76c760ce3 (diff)
khugepaged: fix use-after-free in collapse_huge_page()
hugepage_vma_revalidate() tries to re-check if we still should try to collapse small pages into huge one after the re-acquiring mmap_sem. The problem Dmitry Vyukov reported[1] is that the vma found by hugepage_vma_revalidate() can be suitable for huge pages, but not the same vma we had before dropping mmap_sem. And dereferencing original vma can lead to fun results.. Let's use vma hugepage_vma_revalidate() found instead of assuming it's the same as what we had before the lock was dropped. [1] http://lkml.kernel.org/r/CACT4Y+Z3gigBvhca9kRJFcjX0G70V_nRhbwKBU+yGoESBDKi9Q@mail.gmail.com Link: http://lkml.kernel.org/r/20160907122559.GA6542@black.fi.intel.com Signed-off-by: Kirill A. Shutemov <kirill.shutemov@linux.intel.com> Reported-by: Dmitry Vyukov <dvyukov@google.com> Reviewed-by: Andrea Arcangeli <aarcange@redhat.com> Cc: Ebru Akagunduz <ebru.akagunduz@gmail.com> Cc: Vlastimil Babka <vbabka@suse.cz> Cc: Mel Gorman <mgorman@techsingularity.net> Cc: Johannes Weiner <hannes@cmpxchg.org> Cc: Vegard Nossum <vegard.nossum@oracle.com> Cc: Sasha Levin <levinsasha928@gmail.com> Cc: Konstantin Khlebnikov <koct9i@gmail.com> Cc: Andrey Ryabinin <ryabinin.a.a@gmail.com> Cc: Greg Thelen <gthelen@google.com> Cc: Suleiman Souhlal <suleiman@google.com> Cc: Hugh Dickins <hughd@google.com> Cc: David Rientjes <rientjes@google.com> Cc: syzkaller <syzkaller@googlegroups.com> Cc: Kostya Serebryany <kcc@google.com> Cc: Alexander Potapenko <glider@google.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
-rw-r--r--mm/khugepaged.c15
1 files changed, 8 insertions, 7 deletions
diff --git a/mm/khugepaged.c b/mm/khugepaged.c
index 79c52d0061af..62339bf3c726 100644
--- a/mm/khugepaged.c
+++ b/mm/khugepaged.c
@@ -838,7 +838,8 @@ static bool hugepage_vma_check(struct vm_area_struct *vma)
838 * value (scan code). 838 * value (scan code).
839 */ 839 */
840 840
841static int hugepage_vma_revalidate(struct mm_struct *mm, unsigned long address) 841static int hugepage_vma_revalidate(struct mm_struct *mm, unsigned long address,
842 struct vm_area_struct **vmap)
842{ 843{
843 struct vm_area_struct *vma; 844 struct vm_area_struct *vma;
844 unsigned long hstart, hend; 845 unsigned long hstart, hend;
@@ -846,7 +847,7 @@ static int hugepage_vma_revalidate(struct mm_struct *mm, unsigned long address)
846 if (unlikely(khugepaged_test_exit(mm))) 847 if (unlikely(khugepaged_test_exit(mm)))
847 return SCAN_ANY_PROCESS; 848 return SCAN_ANY_PROCESS;
848 849
849 vma = find_vma(mm, address); 850 *vmap = vma = find_vma(mm, address);
850 if (!vma) 851 if (!vma)
851 return SCAN_VMA_NULL; 852 return SCAN_VMA_NULL;
852 853
@@ -898,7 +899,7 @@ static bool __collapse_huge_page_swapin(struct mm_struct *mm,
898 /* do_swap_page returns VM_FAULT_RETRY with released mmap_sem */ 899 /* do_swap_page returns VM_FAULT_RETRY with released mmap_sem */
899 if (ret & VM_FAULT_RETRY) { 900 if (ret & VM_FAULT_RETRY) {
900 down_read(&mm->mmap_sem); 901 down_read(&mm->mmap_sem);
901 if (hugepage_vma_revalidate(mm, address)) { 902 if (hugepage_vma_revalidate(mm, address, &fe.vma)) {
902 /* vma is no longer available, don't continue to swapin */ 903 /* vma is no longer available, don't continue to swapin */
903 trace_mm_collapse_huge_page_swapin(mm, swapped_in, referenced, 0); 904 trace_mm_collapse_huge_page_swapin(mm, swapped_in, referenced, 0);
904 return false; 905 return false;
@@ -923,7 +924,6 @@ static bool __collapse_huge_page_swapin(struct mm_struct *mm,
923static void collapse_huge_page(struct mm_struct *mm, 924static void collapse_huge_page(struct mm_struct *mm,
924 unsigned long address, 925 unsigned long address,
925 struct page **hpage, 926 struct page **hpage,
926 struct vm_area_struct *vma,
927 int node, int referenced) 927 int node, int referenced)
928{ 928{
929 pmd_t *pmd, _pmd; 929 pmd_t *pmd, _pmd;
@@ -933,6 +933,7 @@ static void collapse_huge_page(struct mm_struct *mm,
933 spinlock_t *pmd_ptl, *pte_ptl; 933 spinlock_t *pmd_ptl, *pte_ptl;
934 int isolated = 0, result = 0; 934 int isolated = 0, result = 0;
935 struct mem_cgroup *memcg; 935 struct mem_cgroup *memcg;
936 struct vm_area_struct *vma;
936 unsigned long mmun_start; /* For mmu_notifiers */ 937 unsigned long mmun_start; /* For mmu_notifiers */
937 unsigned long mmun_end; /* For mmu_notifiers */ 938 unsigned long mmun_end; /* For mmu_notifiers */
938 gfp_t gfp; 939 gfp_t gfp;
@@ -961,7 +962,7 @@ static void collapse_huge_page(struct mm_struct *mm,
961 } 962 }
962 963
963 down_read(&mm->mmap_sem); 964 down_read(&mm->mmap_sem);
964 result = hugepage_vma_revalidate(mm, address); 965 result = hugepage_vma_revalidate(mm, address, &vma);
965 if (result) { 966 if (result) {
966 mem_cgroup_cancel_charge(new_page, memcg, true); 967 mem_cgroup_cancel_charge(new_page, memcg, true);
967 up_read(&mm->mmap_sem); 968 up_read(&mm->mmap_sem);
@@ -994,7 +995,7 @@ static void collapse_huge_page(struct mm_struct *mm,
994 * handled by the anon_vma lock + PG_lock. 995 * handled by the anon_vma lock + PG_lock.
995 */ 996 */
996 down_write(&mm->mmap_sem); 997 down_write(&mm->mmap_sem);
997 result = hugepage_vma_revalidate(mm, address); 998 result = hugepage_vma_revalidate(mm, address, &vma);
998 if (result) 999 if (result)
999 goto out; 1000 goto out;
1000 /* check if the pmd is still valid */ 1001 /* check if the pmd is still valid */
@@ -1202,7 +1203,7 @@ out_unmap:
1202 if (ret) { 1203 if (ret) {
1203 node = khugepaged_find_target_node(); 1204 node = khugepaged_find_target_node();
1204 /* collapse_huge_page will return with the mmap_sem released */ 1205 /* collapse_huge_page will return with the mmap_sem released */
1205 collapse_huge_page(mm, address, hpage, vma, node, referenced); 1206 collapse_huge_page(mm, address, hpage, node, referenced);
1206 } 1207 }
1207out: 1208out:
1208 trace_mm_khugepaged_scan_pmd(mm, page, writable, referenced, 1209 trace_mm_khugepaged_scan_pmd(mm, page, writable, referenced,