From nobody Tue Dec 16 12:33:50 2025 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 24E5EC4332F for ; Thu, 13 Oct 2022 17:52:46 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S229582AbiJMRwo (ORCPT ); Thu, 13 Oct 2022 13:52:44 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:53408 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S229554AbiJMRwk (ORCPT ); Thu, 13 Oct 2022 13:52:40 -0400 Received: from dfw.source.kernel.org (dfw.source.kernel.org [139.178.84.217]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 7803A42D4E; Thu, 13 Oct 2022 10:52:39 -0700 (PDT) Received: from smtp.kernel.org (relay.kernel.org [52.25.139.140]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by dfw.source.kernel.org (Postfix) with ESMTPS id F0CA5618F6; Thu, 13 Oct 2022 17:52:38 +0000 (UTC) Received: by smtp.kernel.org (Postfix) with ESMTPSA id 06DD8C433D6; Thu, 13 Oct 2022 17:52:37 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=linuxfoundation.org; s=korg; t=1665683558; bh=DLTKqalRL/yJy9k0mwrpTytTh7nTjfk/dDsHA3Kqd40=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=GnFw1An3LeANfoVfG+Nzm9BjQOHY4/p0hZ/lM5z6DeGZieeIHIlVaXwgKPkeA1ely SArKtAsDNifOOqGBfUr1VDZDQHwRHrN5y0hKuNHK4N7Bc6fOcmAsqPAwCp6euwAGNT nnym1UKfui2UEnBkfkrGtaHWveFrPlXRwZlBnUp4= From: Greg Kroah-Hartman To: linux-kernel@vger.kernel.org Cc: Greg Kroah-Hartman , stable@vger.kernel.org, Jann Horn , Steven Price , Vlastimil Babka , =?UTF-8?q?Thomas=20Hellstr=C3=B6m?= , Konstantin Khlebnikov , Andrew Morton , Linus Torvalds Subject: [PATCH 5.4 01/38] mm: pagewalk: Fix race between unmap and page walker Date: Thu, 13 Oct 2022 19:52:02 +0200 Message-Id: <20221013175144.300652012@linuxfoundation.org> X-Mailer: git-send-email 2.38.0 In-Reply-To: <20221013175144.245431424@linuxfoundation.org> References: <20221013175144.245431424@linuxfoundation.org> User-Agent: quilt/0.67 X-stable: review X-Patchwork-Hint: ignore MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org From: Steven Price commit 8782fb61cc848364e1e1599d76d3c9dd58a1cc06 upstream. The mmap lock protects the page walker from changes to the page tables during the walk. However a read lock is insufficient to protect those areas which don't have a VMA as munmap() detaches the VMAs before downgrading to a read lock and actually tearing down PTEs/page tables. For users of walk_page_range() the solution is to simply call pte_hole() immediately without checking the actual page tables when a VMA is not present. We now never call __walk_page_range() without a valid vma. For walk_page_range_novma() the locking requirements are tightened to require the mmap write lock to be taken, and then walking the pgd directly with 'no_vma' set. This in turn means that all page walkers either have a valid vma, or it's that special 'novma' case for page table debugging. As a result, all the odd '(!walk->vma && !walk->no_vma)' tests can be removed. Fixes: dd2283f2605e ("mm: mmap: zap pages with read mmap_sem in munmap") Reported-by: Jann Horn Signed-off-by: Steven Price Cc: Vlastimil Babka Cc: Thomas Hellstr=C3=B6m Cc: Konstantin Khlebnikov Cc: Andrew Morton Signed-off-by: Linus Torvalds [manually backported. backport note: walk_page_range_novma() does not exist= in 5.4, so I'm omitting it from the backport] Signed-off-by: Jann Horn Signed-off-by: Greg Kroah-Hartman --- mm/pagewalk.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) --- a/mm/pagewalk.c +++ b/mm/pagewalk.c @@ -38,7 +38,7 @@ static int walk_pmd_range(pud_t *pud, un do { again: next =3D pmd_addr_end(addr, end); - if (pmd_none(*pmd) || !walk->vma) { + if (pmd_none(*pmd)) { if (ops->pte_hole) err =3D ops->pte_hole(addr, next, walk); if (err) @@ -84,7 +84,7 @@ static int walk_pud_range(p4d_t *p4d, un do { again: next =3D pud_addr_end(addr, end); - if (pud_none(*pud) || !walk->vma) { + if (pud_none(*pud)) { if (ops->pte_hole) err =3D ops->pte_hole(addr, next, walk); if (err) @@ -254,7 +254,7 @@ static int __walk_page_range(unsigned lo int err =3D 0; struct vm_area_struct *vma =3D walk->vma; =20 - if (vma && is_vm_hugetlb_page(vma)) { + if (is_vm_hugetlb_page(vma)) { if (walk->ops->hugetlb_entry) err =3D walk_hugetlb_range(start, end, walk); } else @@ -324,9 +324,13 @@ int walk_page_range(struct mm_struct *mm if (!vma) { /* after the last vma */ walk.vma =3D NULL; next =3D end; + if (ops->pte_hole) + err =3D ops->pte_hole(start, next, &walk); } else if (start < vma->vm_start) { /* outside vma */ walk.vma =3D NULL; next =3D min(end, vma->vm_start); + if (ops->pte_hole) + err =3D ops->pte_hole(start, next, &walk); } else { /* inside vma */ walk.vma =3D vma; next =3D min(end, vma->vm_end); @@ -344,9 +348,8 @@ int walk_page_range(struct mm_struct *mm } if (err < 0) break; - } - if (walk.vma || walk.ops->pte_hole) err =3D __walk_page_range(start, next, &walk); + } if (err) break; } while (start =3D next, start < end);