From ca96bd7bf10e62eccc583726be502f219ab02c1e Mon Sep 17 00:00:00 2001 From: Suren Baghdasaryan Date: Mon, 21 Nov 2022 14:08:41 -0800 Subject: [PATCH] ANDROID: mm: avoid using vmacache in lockless vma search When searching vma under RCU protection vmcache should be avoided because a race with munmap() might result in finding a vma and placing it into vmcache after munmap() removed that vma and called vmcache_invalidate. Once that vma is freed, vmcache will be left with an invalid vma pointer. Bug: 257443051 Change-Id: I62438305fcf5139974f4f7d3bae5b22c74084a59 Signed-off-by: Suren Baghdasaryan --- include/linux/mm.h | 2 ++ mm/memory.c | 2 +- mm/mmap.c | 25 +++++++++++++++++-------- mm/nommu.c | 33 +++++++++++++++++++++------------ 4 files changed, 41 insertions(+), 21 deletions(-) diff --git a/include/linux/mm.h b/include/linux/mm.h index 21c8954d4249..fa42ef44c466 100644 --- a/include/linux/mm.h +++ b/include/linux/mm.h @@ -2734,6 +2734,8 @@ extern int expand_upwards(struct vm_area_struct *vma, unsigned long address); #define expand_upwards(vma, address) (0) #endif +extern struct vm_area_struct *find_vma_from_tree(struct mm_struct *mm, + unsigned long addr); /* Look up the first VMA which satisfies addr < vm_end, NULL if none. */ extern struct vm_area_struct * __find_vma(struct mm_struct * mm, unsigned long addr); extern struct vm_area_struct * find_vma_prev(struct mm_struct * mm, unsigned long addr, diff --git a/mm/memory.c b/mm/memory.c index 651978317081..317267711873 100644 --- a/mm/memory.c +++ b/mm/memory.c @@ -216,7 +216,7 @@ struct vm_area_struct *get_vma(struct mm_struct *mm, unsigned long addr) struct vm_area_struct *vma; rcu_read_lock(); - vma = __find_vma(mm, addr); + vma = find_vma_from_tree(mm, addr); if (vma) { if (vma->vm_start > addr || !atomic_inc_unless_negative(&vma->file_ref_count)) diff --git a/mm/mmap.c b/mm/mmap.c index aa61bcb94151..6966807b2cc2 100644 --- a/mm/mmap.c +++ b/mm/mmap.c @@ -2279,16 +2279,10 @@ get_unmapped_area(struct file *file, unsigned long addr, unsigned long len, EXPORT_SYMBOL(get_unmapped_area); -/* Look up the first VMA which satisfies addr < vm_end, NULL if none. */ -struct vm_area_struct *__find_vma(struct mm_struct *mm, unsigned long addr) +struct vm_area_struct *find_vma_from_tree(struct mm_struct *mm, unsigned long addr) { struct rb_node *rb_node; - struct vm_area_struct *vma; - - /* Check the cache first. */ - vma = vmacache_find(mm, addr); - if (likely(vma)) - return vma; + struct vm_area_struct *vma = NULL; rb_node = mm->mm_rb.rb_node; @@ -2306,6 +2300,21 @@ struct vm_area_struct *__find_vma(struct mm_struct *mm, unsigned long addr) rb_node = rb_node->rb_right; } + return vma; +} + +/* Look up the first VMA which satisfies addr < vm_end, NULL if none. */ +struct vm_area_struct *__find_vma(struct mm_struct *mm, unsigned long addr) +{ + struct vm_area_struct *vma; + + /* Check the cache first. */ + vma = vmacache_find(mm, addr); + if (likely(vma)) + return vma; + + vma = find_vma_from_tree(mm, addr); + if (vma) vmacache_update(addr, vma); return vma; diff --git a/mm/nommu.c b/mm/nommu.c index bbe7b1a8c155..f90afb1c8578 100644 --- a/mm/nommu.c +++ b/mm/nommu.c @@ -659,6 +659,22 @@ static void delete_vma(struct mm_struct *mm, struct vm_area_struct *vma) vm_area_free(vma); } +struct vm_area_struct *find_vma_from_tree(struct mm_struct *mm, unsigned long addr) +{ + struct vm_area_struct *vma; + + /* trawl the list (there may be multiple mappings in which addr + * resides) */ + for (vma = mm->mmap; vma; vma = vma->vm_next) { + if (vma->vm_start > addr) + return NULL; + if (vma->vm_end > addr) + return vma; + } + + return NULL; +} + /* * look up the first VMA in which addr resides, NULL if none * - should be called with mm->mmap_lock at least held readlocked @@ -667,23 +683,16 @@ struct vm_area_struct *__find_vma(struct mm_struct *mm, unsigned long addr) { struct vm_area_struct *vma; - /* check the cache first */ + /* Check the cache first. */ vma = vmacache_find(mm, addr); if (likely(vma)) return vma; - /* trawl the list (there may be multiple mappings in which addr - * resides) */ - for (vma = mm->mmap; vma; vma = vma->vm_next) { - if (vma->vm_start > addr) - return NULL; - if (vma->vm_end > addr) { - vmacache_update(addr, vma); - return vma; - } - } + vma = find_vma_from_tree(mm, addr); - return NULL; + if (vma) + vmacache_update(addr, vma); + return vma; } EXPORT_SYMBOL(__find_vma);