From nobody Mon Jun 8 04:24:35 2026 Received: from out-180.mta1.migadu.com (out-180.mta1.migadu.com [95.215.58.180]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 08F4C2D1F44 for ; Tue, 2 Jun 2026 13:09:14 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=95.215.58.180 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1780405756; cv=none; b=F6ZofHM2HRvx2S4IEVLV91ZBQ9EJ50p+V1bN43Rj4GZJGW6cAusWQGfkMqRSeLXoVXPxziJRCfPz+04I0hzJ5FTVqGuLY4MN7p7N7V8VAIsnD5lOWrPOos80j7urXrDiERbbLrJ3Rwxm65ne9t0BrdHHjqF6vTzU0cM1vBZhK3g= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1780405756; c=relaxed/simple; bh=wuTHYJHfr4jnF8zmWavxYOvbdcIrlOWxUzyU6/SvCq4=; h=From:To:Cc:Subject:Date:Message-ID:MIME-Version; b=bKqPEzxhlZzC9bD77DKMdpkoN05nEHlh5hGvEJeQmVLEH4KcDzpvg0OxB4SrVPXmt2RWPz88OcST7wwARS300QKNz05TJNCOhbi3Q9mYVSsP6DbFezh0SCVuHmSOh0OgTEOR4uPXiAgrO99eWFIohAQ26i8SCjQ7ZZsX5BlT37Y= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.dev; spf=pass smtp.mailfrom=linux.dev; dkim=pass (1024-bit key) header.d=linux.dev header.i=@linux.dev header.b=E3xf2kLY; arc=none smtp.client-ip=95.215.58.180 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.dev Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=linux.dev Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=linux.dev header.i=@linux.dev header.b="E3xf2kLY" X-Report-Abuse: Please report any abuse attempt to abuse@migadu.com and include these headers. DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linux.dev; s=key1; t=1780405739; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding; bh=X71Ko2dwlaj1eXjcSWID0o4QdsL8TgXl6KYElkJpbB4=; b=E3xf2kLYo/hEXeRgNzPWIANuBWg5nOEsdshaf/ZbrauMZNH1+5xEYvCsid3Lh37+3Etk7j MO1Q+tIpL+DqcDqabvNVwPuxqYLzZbHhBJNlQi0w9NwJUf4izgZTbbWzsTAQkCV1FBFcji NaTNfNlC2SqgPbSQFzE2XlG4G/5K96s= From: Kaitao Cheng To: Andrew Morton , Vlastimil Babka , Suren Baghdasaryan , Michal Hocko , Brendan Jackman , Johannes Weiner , Zi Yan Cc: Liu Shixin , David Hildenbrand , Oscar Salvador , muchun.song@linux.dev, linux-mm@kvack.org, linux-kernel@vger.kernel.org, Kaitao Cheng Subject: [PATCH v2] mm: page_isolation: avoid unsafe folio reads while scanning compound pages Date: Tue, 2 Jun 2026 21:07:55 +0800 Message-ID: <20260602130755.38794-1-kaitao.cheng@linux.dev> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Migadu-Flow: FLOW_OUT Content-Type: text/plain; charset="utf-8" From: Kaitao Cheng page_is_unmovable() can inspect compound pages without holding a folio reference or any lock. The folio can therefore be freed, split or reused while the scanner is still looking at it. The existing HugeTLB handling already avoids folio_hstate() for this reason, but it still derives the hstate from folio_size() and later derives the scan step from folio_nr_pages() and folio_page_idx(). These helpers rely on the folio still being a valid folio head. If the folio changed concurrently, the scanner can read inconsistent folio metadata and compute a wrong step. In the worst case, folio_nr_pages() can return 1 for what used to be a tail page and the subtraction from folio_page_idx() can underflow. There is a similar issue for non-Hugetlb compound pages: folio_test_lru() expects a valid folio. If the previously observed head page has been reused as a tail page of another compound page, the folio flag checks can trigger VM_BUG_ON_PGFLAGS(). Read the compound order once with compound_order(), reject obviously bogus orders, and derive the hstate and scan step from that order instead of querying folio size information again. Also use PageLRU(page), which is safe for the page being scanned, instead of folio_test_lru() on a potentially stale folio pointer. Treat an unknown HugeTLB hstate as unmovable so the scanner does not try to skip over an unstable HugeTLB folio. Fixes: a0a9f2180b90 ("mm: page_isolation: avoid calling folio_hstate() with= out hugetlb_lock") Signed-off-by: Kaitao Cheng Acked-by: David Hildenbrand (Arm) Acked-by: Oscar Salvador (SUSE) Reviewed-by: Zi Yan --- Changes in v2: - Avoid unsafe folio metadata reads in the unlocked scanner by deriving the hstate and scan step from compound_order(). (David Hildenbrand, Andrew Morton) - Treat invalid compound orders or unknown HugeTLB hstates as unmovable. - Use PageLRU(page) instead of folio_test_lru(folio) to avoid folio flag checks on a stale folio pointer. () - Update the commit log (David Hildenbrand) Link to v1: https://lore.kernel.org/all/20260519121646.40833-1-kaitao.cheng@linux.dev/ --- mm/page_isolation.c | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/mm/page_isolation.c b/mm/page_isolation.c index 7a9d631945a3..32ce8a7d9df3 100644 --- a/mm/page_isolation.c +++ b/mm/page_isolation.c @@ -41,8 +41,14 @@ bool page_is_unmovable(struct zone *zone, struct page *p= age, * We need not scan over tail pages because we don't * handle each tail page individually in migration. */ - if (PageHuge(page) || PageCompound(page)) { + if (PageCompound(page)) { struct folio *folio =3D page_folio(page); + unsigned long nr_pages, pfn; + unsigned int order; + + order =3D compound_order(&folio->page); + if (order > MAX_FOLIO_ORDER) + return true; =20 if (folio_test_hugetlb(folio)) { struct hstate *h; @@ -54,15 +60,16 @@ bool page_is_unmovable(struct zone *zone, struct page *= page, * The huge page may be freed so can not * use folio_hstate() directly. */ - h =3D size_to_hstate(folio_size(folio)); - if (h && !hugepage_migration_supported(h)) + h =3D size_to_hstate(PAGE_SIZE << order); + if (!h || !hugepage_migration_supported(h)) return true; - - } else if (!folio_test_lru(folio)) { + } else if (!PageLRU(page)) { return true; } =20 - *step =3D folio_nr_pages(folio) - folio_page_idx(folio, page); + nr_pages =3D 1UL << order; + pfn =3D page_to_pfn(page); + *step =3D (pfn | (nr_pages - 1)) + 1 - pfn; return false; } =20 --=20 2.43.0