All of lore.kernel.org
 help / color / mirror / Atom feed
From: sashiko-bot@kernel.org
To: "Yuanhe Shu" <xiangzao@linux.alibaba.com>,
	akpm@linux-foundation.org, david@kernel.org
Cc: "Huang, Ying" <ying.huang@linux.alibaba.com>,
	"Kaitao Cheng" <kaitao.cheng@linux.dev>,
	vbabka@kernel.org, ljs@kernel.org, liam@infradead.org,
	rppt@kernel.org, surenb@google.com, mhocko@suse.com,
	linmiaohe@huawei.com, nao.horiguchi@gmail.com, ziy@nvidia.com,
	wangkefeng.wang@huawei.com, tujinjiang@huawei.com,
	mgorman@techsingularity.net, chengkaitao@kylinos.cn,
	muchun.song@linux.dev, linux-mm@kvack.org
Subject: Re: [PATCH] mm/highmem: refuse to copy hwpoisoned folios in the !copy_mc fallbacks
Date: Tue, 27 Oct 2026 02:57:43 +0000	[thread overview]
Message-ID: <sashiko-outbox-186037@kernel.org> (raw)
In-Reply-To: <20261026025724.1430747-1-xiangzao@linux.alibaba.com>

Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
- [Critical] mm/highmem: wrong hwpoison granularity in copy_mc_*_highpage() fallbacks
- [High] mm/highmem: copy_mc_*_highpage() reads poisoned page before flag check
--

commit e960425b93cf423ea993320abfce6ba212a1b8fe
Author: Yuanhe Shu <xiangzao@linux.alibaba.com>

mm/highmem: refuse to copy hwpoisoned folios in the !copy_mc fallbacks

This commit updates the !copy_mc_to_kernel fallbacks for
copy_mc_user_highpage() and copy_mc_highpage() to check
folio_contain_hwpoisoned_page(page_folio(from)) after copying the page
and return PAGE_SIZE if set.

Link: https://lore.kernel.org/r/877bjowf9i.fsf@DESKTOP-5N7EMDA/

> diff --git a/include/linux/highmem.h b/include/linux/highmem.h
> index d7aac9de1c8a4..f90f0c6204a4e 100644
> --- a/include/linux/highmem.h
> +++ b/include/linux/highmem.h
> @@ -474,12 +474,21 @@ static inline int copy_mc_user_highpage(struct page *to, struct page *from,
>  					unsigned long vaddr, struct vm_area_struct *vma)
>  {
>  	copy_user_highpage(to, from, vaddr, vma);
> +	/*
> +	 * The flag can be set while the copy runs, so check after it;
> +	 * a flag landing later can no longer have corrupted this copy.
> +	 */

[Severity: High]
What happens in copy_mc_user_highpage() and copy_mc_highpage() if the
source page already has PG_hwpoison set from a real hardware memory error
before the copy starts (for example, reported earlier via ACPI APEI GHES, a
patrol scrubber, or a prior user-mode fault where memory_failure() set
PG_hwpoison before locking or unmapping the folio)?

On architectures without copy_mc_to_kernel (such as arm64, riscv, and
loongarch), copy_user_highpage() and copy_highpage() map the page with
kmap_local_page(from) and perform a plain kernel memory read via
copy_user_page() or copy_page() without exception-table recovery for
hardware memory errors.

Callers such as folio_mc_copy(), wp_page_copy() -> __wp_page_copy_user(),
copy_present_page(), collapse_file(), and __collapse_huge_page_copy() rely
on copy_mc_user_highpage() and copy_mc_highpage() to refuse poisoned pages.
If the check only runs after the copy, won't reading an already-poisoned
physical page in kernel mode trigger an unrecoverable kernel-mode fault
(such as an EL1 synchronous external abort on arm64) and panic the kernel
before reaching the post-copy check?

Should both functions check whether the source page is hwpoisoned before
the copy in addition to after the copy?

> +	if (folio_contain_hwpoisoned_page(page_folio(from)))
> +		return PAGE_SIZE;
>  	return 0;
>  }
>  
>  static inline int copy_mc_highpage(struct page *to, struct page *from)
>  {
>  	copy_highpage(to, from);
> +	/* Same reasoning as in copy_mc_user_highpage(). */
> +	if (folio_contain_hwpoisoned_page(page_folio(from)))
> +		return PAGE_SIZE;

[Severity: Critical]
Is folio_contain_hwpoisoned_page(page_folio(from)) the right granularity
for single-page copy helpers like copy_mc_user_highpage() and
copy_mc_highpage()?

In include/linux/page-flags.h:

static inline bool folio_contain_hwpoisoned_page(struct folio *folio)
{
	return folio_test_hwpoison(folio) ||
	    (folio_test_large(folio) && folio_test_has_hwpoisoned(folio));
}

Because FOLIO_PF_ANY is 0, folio_test_hwpoison(folio) only tests
PG_hwpoison on the head page (&folio->page[0]), not on a tail page passed
as from. For any tail page of a large folio,
folio_contain_hwpoisoned_page(page_folio(from)) relies purely on the
folio-wide PG_has_hwpoisoned flag. Doesn't this cause both false negatives
and false positives?

First, for tail pages with PG_hwpoison set where PG_has_hwpoisoned is not
set, folio_contain_hwpoisoned_page(page_folio(from)) returns false and the
poisoned tail page is silently copied:

1. In mm/memory-failure.c:memory_failure(), TestSetPageHWPoison(p) sets
   PG_hwpoison on the target tail page before get_hwpoison_page() and
   folio_lock(folio), while folio_set_has_hwpoisoned(folio) is only called
   after acquiring and releasing folio_lock(folio):

   memory_failure()
       if (TestSetPageHWPoison(p))
           ...
       res = get_hwpoison_page(p, flags);
       ...
       folio_lock(folio);
       ...
       folio_unlock(folio);
       if (folio_test_large(folio)) {
           ...
           folio_set_has_hwpoisoned(folio);

   Callers holding folio_lock on the source folio during the copy -- such
   as migrate_folio() / filemap_migrate_folio() / __migrate_folio() ->
   folio_mc_copy(), do_cow_fault(), collapse_file(), and
   __collapse_huge_page_copy() -- block a concurrent memory_failure() at
   folio_lock(folio) after PageHWPoison(from) is already set on the tail
   page, before folio_set_has_hwpoisoned(folio) can run.

2. If memory_failure() (e.g., via debugfs corrupt-pfn without
   MF_COUNT_INCREASED) hits a tail page while a large folio is isolated
   from the LRU for migration or khugepaged, TestSetPageHWPoison(p) sets
   PG_hwpoison on the tail page, get_hwpoison_page() fails because
   HWPoisonHandlable() returns false for !PageLRU, and memory_failure()
   aborts without ever setting PG_has_hwpoisoned.

3. When CONFIG_TRANSPARENT_HUGEPAGE=n and CONFIG_MEMORY_FAILURE=y,
   folio_test_has_hwpoisoned() is compiled as FOLIO_FLAG_FALSE (always
   false), so poisoned tail pages of large page-cache folios are never
   detected, and mf_dax_kill_procs() sets PageHWPoison(page) on FS-DAX
   subpages without setting PG_has_hwpoisoned.

Second, for a non-hugetlb large folio that has PG_has_hwpoisoned set
because one subpage is poisoned (and could not be split to order 0, e.g.
due to extra references or min_order_for_split(folio) > 0 on a large block
size filesystem), unpoisoned subpages remain PTE-mappable and accessible.
When a single-page copy runs on a healthy subpage of that folio via
do_cow_fault(), wp_page_copy() -> __wp_page_copy_user(), or
copy_present_page() during fork(),
folio_contain_hwpoisoned_page(page_folio(from)) returns true and fails the
operation with VM_FAULT_HWPOISON (SIGBUS) or -EHWPOISON even though the
copied subpage from is not poisoned.

Would using is_page_hwpoison(from) in include/linux/page-flags.h instead of
folio_contain_hwpoisoned_page(page_folio(from)) avoid both problems by
checking PageHWPoison(from) on the specific subpage being copied while
still checking the head page for hugetlb folios?

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20261026025724.1430747-1-xiangzao@linux.alibaba.com?part=1


      parent reply	other threads:[~2026-10-27  2:57 UTC|newest]

Thread overview: 11+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-10-26  2:57 [PATCH] mm/highmem: refuse to copy hwpoisoned folios in the !copy_mc fallbacks Yuanhe Shu
2026-10-26  9:55 ` David Hildenbrand (Arm)
2026-10-27 12:13   ` Yuanhe Shu
2026-10-26 11:21 ` Huang, Ying
2026-10-26 12:17   ` Garg, Shivank
2026-10-27  1:33     ` Huang, Ying
2026-10-27  1:40   ` Huang, Ying
2026-10-27 12:14   ` Yuanhe Shu
2026-10-26 12:09 ` Garg, Shivank
2026-10-27 12:20   ` Yuanhe Shu
2026-10-27  2:57 ` sashiko-bot [this message]

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=sashiko-outbox-186037@kernel.org \
    --to=sashiko-bot@kernel.org \
    --cc=akpm@linux-foundation.org \
    --cc=chengkaitao@kylinos.cn \
    --cc=david@kernel.org \
    --cc=kaitao.cheng@linux.dev \
    --cc=liam@infradead.org \
    --cc=linmiaohe@huawei.com \
    --cc=linux-mm@kvack.org \
    --cc=ljs@kernel.org \
    --cc=mgorman@techsingularity.net \
    --cc=mhocko@suse.com \
    --cc=muchun.song@linux.dev \
    --cc=nao.horiguchi@gmail.com \
    --cc=rppt@kernel.org \
    --cc=sashiko-reviews@lists.linux.dev \
    --cc=surenb@google.com \
    --cc=tujinjiang@huawei.com \
    --cc=vbabka@kernel.org \
    --cc=wangkefeng.wang@huawei.com \
    --cc=xiangzao@linux.alibaba.com \
    --cc=ying.huang@linux.alibaba.com \
    --cc=ziy@nvidia.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.