diff mbox series

mm/rmap: ensure the validity of mapping vma which referenced an anon page

Message ID 1583912713-30778-1-git-send-email-qiwuchen55@gmail.com (mailing list archive)
State New, archived
Headers show
Series mm/rmap: ensure the validity of mapping vma which referenced an anon page | expand

Commit Message

chenqiwu March 11, 2020, 7:45 a.m. UTC
From: chenqiwu <chenqiwu@xiaomi.com>

When finding all the mapping vmas for an anon page by anon_vma, there
is a panic risk that one mapping vma or its vm_mm has been released by
someone. Like the following crash during kswapd reclaiming pages:

Unable to handle kernel NULL pointer dereference at virtual address
00000048
PC is at page_vma_mapped_walk+0x54/0x16c
LR is at page_referenced_one+0x44/0x140
[......]
CPU: 1 PID: 161 Comm: kswapd0
Call trace:
[<ffffff9a080832d0>] el1_da+0x24/0x3c
[<ffffff9a0823ea18>] page_vma_mapped_walk+0x54/0x16c
[<ffffff9a0823fd8c>] page_referenced_one+0x44/0x140
[<ffffff9a08240ef0>] rmap_walk_anon+0x124/0x168
[<ffffff9a0823fcfc>] page_referenced+0x144/0x190
[<ffffff9a08213e6c>] shrink_active_list+0x25c/0x478
[<ffffff9a082126c8>] kswapd+0x7b0/0x9c8
[<ffffff9a080daffc>] kthread+0x154/0x18c
[<ffffff9a0808563c>] ret_from_fork+0x10/0x18

The PC is pointed to the following code line:
bool page_vma_mapped_walk(struct page_vma_mapped_walk *pvmw)
{
	struct mm_struct *mm = pvmw->vma->vm_mm;
	......
	pgd = pgd_offset(mm, pvmw->address);	//PC
	......
}

Because the current pvmw->vma->vm_mm is a kernel NULL pointer, which
causing crash when pgd_offset() dereferences the mm pointer.

This patch fixes the problem by ensuring that both the mapping vma
and its vm_mm are valid. If not, we just continue to traverse the
anon_vma->rb_root to avoid the potential junk pointer dereference.

Signed-off-by: chenqiwu <chenqiwu@xiaomi.com>
---
 mm/rmap.c | 3 +++
 1 file changed, 3 insertions(+)

Comments

Kirill A . Shutemov March 11, 2020, 9:19 a.m. UTC | #1
On Wed, Mar 11, 2020 at 03:45:13PM +0800, qiwuchen55@gmail.com wrote:
> From: chenqiwu <chenqiwu@xiaomi.com>
> 
> When finding all the mapping vmas for an anon page by anon_vma, there
> is a panic risk that one mapping vma or its vm_mm has been released by
> someone.

What? Who would be able to release the VMA or the mm_struct? We hold
anon_vma lock. It doesn't make sense to me. Something else is broken.

> Like the following crash during kswapd reclaiming pages:
> 
> Unable to handle kernel NULL pointer dereference at virtual address
> 00000048
> PC is at page_vma_mapped_walk+0x54/0x16c
> LR is at page_referenced_one+0x44/0x140
> [......]
> CPU: 1 PID: 161 Comm: kswapd0
> Call trace:
> [<ffffff9a080832d0>] el1_da+0x24/0x3c
> [<ffffff9a0823ea18>] page_vma_mapped_walk+0x54/0x16c
> [<ffffff9a0823fd8c>] page_referenced_one+0x44/0x140
> [<ffffff9a08240ef0>] rmap_walk_anon+0x124/0x168
> [<ffffff9a0823fcfc>] page_referenced+0x144/0x190
> [<ffffff9a08213e6c>] shrink_active_list+0x25c/0x478
> [<ffffff9a082126c8>] kswapd+0x7b0/0x9c8
> [<ffffff9a080daffc>] kthread+0x154/0x18c
> [<ffffff9a0808563c>] ret_from_fork+0x10/0x18
> 
> The PC is pointed to the following code line:
> bool page_vma_mapped_walk(struct page_vma_mapped_walk *pvmw)
> {
> 	struct mm_struct *mm = pvmw->vma->vm_mm;
> 	......
> 	pgd = pgd_offset(mm, pvmw->address);	//PC
> 	......
> }
> 
> Because the current pvmw->vma->vm_mm is a kernel NULL pointer, which
> causing crash when pgd_offset() dereferences the mm pointer.
> 
> This patch fixes the problem by ensuring that both the mapping vma
> and its vm_mm are valid. If not, we just continue to traverse the
> anon_vma->rb_root to avoid the potential junk pointer dereference.
> 
> Signed-off-by: chenqiwu <chenqiwu@xiaomi.com>
> ---
>  mm/rmap.c | 3 +++
>  1 file changed, 3 insertions(+)
> 
> diff --git a/mm/rmap.c b/mm/rmap.c
> index b3e3819..fc42ca2 100644
> --- a/mm/rmap.c
> +++ b/mm/rmap.c
> @@ -1863,6 +1863,9 @@ static void rmap_walk_anon(struct page *page, struct rmap_walk_control *rwc,
>  		if (rwc->invalid_vma && rwc->invalid_vma(vma, rwc->arg))
>  			continue;
>  
> +		if (!vma && !vma->vm_mm)
> +			continue;
> +

Even if the premis of the patch is true, 'vma' got used twice in the loop
iteration before you check if it's non-NULL.

>  		if (!rwc->rmap_one(page, vma, address, rwc->arg))
>  			break;
>  		if (rwc->done && rwc->done(page))
> -- 
> 1.9.1
> 
>
chenqiwu March 11, 2020, 10:31 a.m. UTC | #2
On Wed, Mar 11, 2020 at 12:19:43PM +0300, Kirill A. Shutemov wrote:
> On Wed, Mar 11, 2020 at 03:45:13PM +0800, qiwuchen55@gmail.com wrote:
> > From: chenqiwu <chenqiwu@xiaomi.com>
> > 
> > When finding all the mapping vmas for an anon page by anon_vma, there
> > is a panic risk that one mapping vma or its vm_mm has been released by
> > someone.
> 
> What? Who would be able to release the VMA or the mm_struct? We hold
> anon_vma lock. It doesn't make sense to me. Something else is broken.
>

There is only log (no kdump) in my hand. It's hard to say who released the
VMA's mm_struct before we hold the anon_vma lock.
diff mbox series

Patch

diff --git a/mm/rmap.c b/mm/rmap.c
index b3e3819..fc42ca2 100644
--- a/mm/rmap.c
+++ b/mm/rmap.c
@@ -1863,6 +1863,9 @@  static void rmap_walk_anon(struct page *page, struct rmap_walk_control *rwc,
 		if (rwc->invalid_vma && rwc->invalid_vma(vma, rwc->arg))
 			continue;
 
+		if (!vma && !vma->vm_mm)
+			continue;
+
 		if (!rwc->rmap_one(page, vma, address, rwc->arg))
 			break;
 		if (rwc->done && rwc->done(page))