From: Nguyen Ngoc Thang <ngocthang2710.1999@gmail.com>
To: akpm@linux-foundation.org, david@kernel.org
Cc: Nguyen Ngoc Thang <ngocthang2710.1999@gmail.com>,
ljs@kernel.org, liam@infradead.org, vbabka@kernel.org,
rppt@kernel.org, surenb@google.com, mhocko@suse.com,
peterx@redhat.com, dave.hansen@linux.intel.com,
linux-mm@kvack.org, linux-kernel@vger.kernel.org,
syzbot+49b1021becba70c1f3f6@syzkaller.appspotmail.com
Subject: [PATCH] mm: don't ioremap COWed anon pages in generic_access_phys()
Date: Thu, 1 Oct 2026 22:25:24 +0700 [thread overview]
Message-ID: <20261001152524.171115-1-ngocthang2710.1999@gmail.com> (raw)
In-Reply-To: <699ae98c.050a0220.340abe.0d31.GAE@google.com>
A MAP_PRIVATE mapping of iomem (e.g. a PCI sysfs resourceN file) is a
COW pfnmap: a write fault replaces the pfn with an anonymous page, which
remap_pfn_range() allows by keeping vm_pgoff equal to the base pfn.
generic_access_phys() does not tell those COWed pages apart from the
original pfns and ioremaps whatever the PTE points to. Reading such an
address via /proc/pid/mem or ptrace then ioremaps RAM:
ioremap on RAM at 0x0000000045623000 - 0x0000000045623fff
WARNING: arch/x86/mm/ioremap.c:216 at __ioremap_caller.isra.0+0x4c2/0x5f0
Call Trace:
generic_access_phys+0x130/0x4d0 mm/memory.c:7178
kernfs_vma_access+0x1ce/0x280 fs/kernfs/file.c:437
__access_remote_vm+0x58f/0x890 mm/memory.c:7256
mem_rw+0x2a1/0x670 fs/proc/base.c:912
Reject COWed pages using the same linearity rule vm_normal_page() uses.
All generic_access_phys() users set up the mapping with a full-vma
(io_)remap_pfn_range(), so the rule holds for them. The access already
fails on x86 and arm64, whose ioremap refuses RAM; elsewhere it no longer
reads the anon page through a device-memory alias.
Fixes: 28b2ee20c7cb ("access_process_vm device memory infrastructure")
Reported-by: syzbot+49b1021becba70c1f3f6@syzkaller.appspotmail.com
Closes: https://syzkaller.appspot.com/bug?extid=49b1021becba70c1f3f6
Signed-off-by: Nguyen Ngoc Thang <ngocthang2710.1999@gmail.com>
---
This answers Dave's question on the syzbot thread of how a PCI BAR can
point at RAM: it doesn't. The repro mmap()s resource1 MAP_PRIVATE with
PROT_WRITE at address 0, then (via syz_ublk_add_dev() with NULL args)
writes to address 0. That write COWs the BAR page into an anonymous
page, and the later /proc/self/mem read hands its pfn to ioremap.
Forbidding MAP_PRIVATE on BARs would not cover /dev/mem, uio, cdx or
dfl-afu, which share this ->access hook, and private pfnmap COW is
deliberately supported (see get_remap_pgoff()).
Tested in QEMU (q35, virtio-net at 00:03.0) with a small program that
maps resource1 shared and private, COWs the private page and reads both
via pread(/proc/self/mem):
before after
shared 4, 0xfee01000 4, 0xfee01000
private 4, 0xfee01000 4, 0xfee01000
private-cowed -1 EIO + WARN -1 EIO, no WARN
(pread() sees EIO either way; internally generic_access_phys() went from
ioremap WARN + -ENOMEM to -EINVAL.)
plus 4 x 500 parallel runs on the patched kernel, no warnings.
Note: the syzbot report also groups an unrelated signature under the
same title (pcibios_device_add() -> memremap() of setup_data on PCI
rescan); this patch does not address that one.
mm/memory.c | 11 +++++++++++
1 file changed, 11 insertions(+)
diff --git a/mm/memory.c b/mm/memory.c
index 8b0c2c735d3d..8551b144028d 100644
--- a/mm/memory.c
+++ b/mm/memory.c
@@ -7141,6 +7141,14 @@ void follow_pfnmap_end(struct follow_pfnmap_args *args)
EXPORT_SYMBOL_GPL(follow_pfnmap_end);
#ifdef CONFIG_HAVE_IOREMAP_PROT
+/* A write fault on a private pfnmap replaces the pfn with an anon page. */
+static bool pfnmap_pfn_is_cowed(struct vm_area_struct *vma, unsigned long addr,
+ unsigned long pfn)
+{
+ return (vma->vm_flags & VM_PFNMAP) && vma_is_cow_mapping(vma) &&
+ pfn != linear_page_index(vma, addr);
+}
+
/**
* generic_access_phys - generic implementation for iomem mmap access
* @vma: the vma to access
@@ -7175,6 +7183,9 @@ int generic_access_phys(struct vm_area_struct *vma, unsigned long addr,
if ((write & FOLL_WRITE) && !writable)
return -EINVAL;
+ if (pfnmap_pfn_is_cowed(vma, addr, phys_addr >> PAGE_SHIFT))
+ return -EINVAL;
+
maddr = ioremap_prot(phys_addr, PAGE_ALIGN(len + offset), prot);
if (!maddr)
return -ENOMEM;
--
2.43.0
next parent reply other threads:[~2026-10-01 15:25 UTC|newest]
Thread overview: 10+ messages / expand[flat|nested] mbox.gz Atom feed top
[not found] <699ae98c.050a0220.340abe.0d31.GAE@google.com>
2026-10-01 15:25 ` Nguyen Ngoc Thang [this message]
2026-10-01 20:22 ` [PATCH] mm: don't ioremap COWed anon pages in generic_access_phys() David Hildenbrand (Arm)
2026-10-02 8:38 ` Lorenzo Stoakes (ARM)
2026-10-02 10:02 ` David Hildenbrand (Arm)
2026-10-02 10:04 ` David Hildenbrand (Arm)
2026-10-02 10:09 ` Lorenzo Stoakes (ARM)
2026-10-02 12:19 ` David Hildenbrand (Arm)
2026-10-02 14:03 ` Lorenzo Stoakes (ARM)
2026-10-02 14:26 ` David Hildenbrand (Arm)
2026-10-02 10:07 ` Lorenzo Stoakes (ARM)
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=20261001152524.171115-1-ngocthang2710.1999@gmail.com \
--to=ngocthang2710.1999@gmail.com \
--cc=akpm@linux-foundation.org \
--cc=dave.hansen@linux.intel.com \
--cc=david@kernel.org \
--cc=liam@infradead.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-mm@kvack.org \
--cc=ljs@kernel.org \
--cc=mhocko@suse.com \
--cc=peterx@redhat.com \
--cc=rppt@kernel.org \
--cc=surenb@google.com \
--cc=syzbot+49b1021becba70c1f3f6@syzkaller.appspotmail.com \
--cc=vbabka@kernel.org \
/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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox