* [PATCH 0/2] mm,kernfs: Fix stale userspace mappings after sysfs file removal
@ 2026-07-21 18:52 Krzysztof Wilczyński
2026-07-21 18:52 ` [PATCH 1/2] mm: Add unmap_mapping_file() helper Krzysztof Wilczyński
2026-07-21 18:52 ` [PATCH 2/2] kernfs: Unmap mmaps of removed files via file->f_mapping Krzysztof Wilczyński
0 siblings, 2 replies; 5+ messages in thread
From: Krzysztof Wilczyński @ 2026-07-21 18:52 UTC (permalink / raw)
To: Andrew Morton, David Hildenbrand, Greg Kroah-Hartman, Tejun Heo,
Bjorn Helgaas
Cc: Bjorn Helgaas, Manivannan Sadhasivam, Lorenzo Pieralisi,
Liam R . Howlett, Dan Williams, Lorenzo Stoakes, Michal Hocko,
Mike Rapoport, Simona Vetter, Suren Baghdasaryan, Vlastimil Babka,
linux-pci, linux-mm, driver-core
Hello,
PCI resource sysfs files swap their f_mapping to iomem_get_mapping() at
open time so that revoke_iomem() can hole-punch userspace mappings when
a driver claims the BAR (636b21b50152, "PCI: Revoke mappings like
devmem"). Their VMAs therefore live on the shared iomem address_space,
which kernfs_drain_open_files() does not walk, unmapping the sysfs
inode's own i_mapping instead. Userspace thus keeps live mappings of
PCI BARs across device removal and resizable-BAR resize, with stale
PTEs into physical address space the kernel may have reassigned.
After the device is removed, a read from a resource0 mapping returns
the previous register value instead of faulting.
The first patch adds unmap_mapping_file(), which zaps VMAs selected by
the originating struct file rather than by address space window, with
a stub for !CONFIG_MMU builds. The second uses it in the kernfs
drain for files with a swapped f_mapping, keeping the existing
unmap_mapping_range() path for everything else.
Before 636b21b50152 these VMAs were attached to the sysfs inode and the
drain did zap them, so this restores the earlier teardown semantics
while keeping the driver-claim revocation intact. Files without an
f_mapping swap (p2pdma, cgroup, everything else) see no change. The
same access now raises SIGBUS.
BAR resize is covered by the same path, since resize removes the
resource groups. /dev/mem and /proc/bus/pci mappings of the same
BAR are unaffected.
Krzysztof Wilczyński (2):
mm: Add unmap_mapping_file() helper
kernfs: Unmap mmaps of removed files via file->f_mapping
fs/kernfs/file.c | 5 ++++-
include/linux/mm.h | 2 ++
mm/memory.c | 26 ++++++++++++++++++++++++++
3 files changed, 32 insertions(+), 1 deletion(-)
--
2.55.0
^ permalink raw reply [flat|nested] 5+ messages in thread
* [PATCH 1/2] mm: Add unmap_mapping_file() helper
2026-07-21 18:52 [PATCH 0/2] mm,kernfs: Fix stale userspace mappings after sysfs file removal Krzysztof Wilczyński
@ 2026-07-21 18:52 ` Krzysztof Wilczyński
2026-07-21 19:19 ` David Hildenbrand (Arm)
2026-07-21 18:52 ` [PATCH 2/2] kernfs: Unmap mmaps of removed files via file->f_mapping Krzysztof Wilczyński
1 sibling, 1 reply; 5+ messages in thread
From: Krzysztof Wilczyński @ 2026-07-21 18:52 UTC (permalink / raw)
To: Andrew Morton, David Hildenbrand, Greg Kroah-Hartman, Tejun Heo,
Bjorn Helgaas
Cc: Bjorn Helgaas, Manivannan Sadhasivam, Lorenzo Pieralisi,
Liam R . Howlett, Dan Williams, Lorenzo Stoakes, Michal Hocko,
Mike Rapoport, Simona Vetter, Suren Baghdasaryan, Vlastimil Babka,
linux-pci, linux-mm, driver-core
Currently, unmap_mapping_pages() and unmap_mapping_range() unmap
a page offset window of an address_space, and teardown paths such
as kernfs_drain_open_files() rely on them to remove the mappings
of a file that is going away.
Files with f_mapping swapped to a shared address space at open time,
such as PCI resource files using iomem_get_mapping(), have their
VMAs attached to the shared mapping instead of the file's own inode
mapping, so a teardown that unmaps the inode's address space cannot
reach them, and they stay live after the file is removed. As such,
the only correct discriminator for these VMAs is the originating
struct file recorded in vma->vm_file.
Add unmap_mapping_file(), which walks the VMA interval tree of the
file's f_mapping under i_mmap_lock_read() and zaps every VMA with
vm_file set to the given file, using the same per-VMA zap teardown
as unmap_mapping_pages().
Signed-off-by: Krzysztof Wilczyński <kwilczynski@kernel.org>
---
include/linux/mm.h | 2 ++
mm/memory.c | 26 ++++++++++++++++++++++++++
2 files changed, 28 insertions(+)
diff --git a/include/linux/mm.h b/include/linux/mm.h
index 485df9c2dbdd..e882a03eaddd 100644
--- a/include/linux/mm.h
+++ b/include/linux/mm.h
@@ -3183,6 +3183,7 @@ extern int fixup_user_fault(struct mm_struct *mm,
bool *unlocked);
void unmap_mapping_pages(struct address_space *mapping,
pgoff_t start, pgoff_t nr, bool even_cows);
+void unmap_mapping_file(struct file *file);
void unmap_mapping_range(struct address_space *mapping,
loff_t const holebegin, loff_t const holelen, int even_cows);
#else
@@ -3203,6 +3204,7 @@ static inline int fixup_user_fault(struct mm_struct *mm, unsigned long address,
}
static inline void unmap_mapping_pages(struct address_space *mapping,
pgoff_t start, pgoff_t nr, bool even_cows) { }
+static inline void unmap_mapping_file(struct file *file) { }
static inline void unmap_mapping_range(struct address_space *mapping,
loff_t const holebegin, loff_t const holelen, int even_cows) { }
#endif
diff --git a/mm/memory.c b/mm/memory.c
index ff338c2abe92..f9cfb88ac4f7 100644
--- a/mm/memory.c
+++ b/mm/memory.c
@@ -4423,6 +4423,32 @@ void unmap_mapping_pages(struct address_space *mapping, pgoff_t start,
}
EXPORT_SYMBOL_GPL(unmap_mapping_pages);
+/**
+ * unmap_mapping_file() - Unmap all mmaps of an open file.
+ * @file: The file to unmap.
+ *
+ * Unmap every VMA with vm_file set to @file, regardless of the address
+ * space it is attached to. This also covers files with f_mapping
+ * swapped to a different address space at open time, since such VMAs
+ * cannot be found through the file's own inode mapping.
+ */
+void unmap_mapping_file(struct file *file)
+{
+ struct address_space *mapping = file->f_mapping;
+ struct vm_area_struct *vma;
+
+ i_mmap_lock_read(mapping);
+ if (unlikely(mapping_mapped(mapping))) {
+ vma_interval_tree_foreach(vma, &mapping->i_mmap, 0, ULONG_MAX) {
+ if (vma->vm_file != file)
+ continue;
+ zap_vma(vma);
+ }
+ }
+ i_mmap_unlock_read(mapping);
+}
+EXPORT_SYMBOL_GPL(unmap_mapping_file);
+
/**
* unmap_mapping_range - unmap the portion of all mmaps in the specified
* address_space corresponding to the specified byte range in the underlying
--
2.55.0
^ permalink raw reply related [flat|nested] 5+ messages in thread
* [PATCH 2/2] kernfs: Unmap mmaps of removed files via file->f_mapping
2026-07-21 18:52 [PATCH 0/2] mm,kernfs: Fix stale userspace mappings after sysfs file removal Krzysztof Wilczyński
2026-07-21 18:52 ` [PATCH 1/2] mm: Add unmap_mapping_file() helper Krzysztof Wilczyński
@ 2026-07-21 18:52 ` Krzysztof Wilczyński
1 sibling, 0 replies; 5+ messages in thread
From: Krzysztof Wilczyński @ 2026-07-21 18:52 UTC (permalink / raw)
To: Andrew Morton, David Hildenbrand, Greg Kroah-Hartman, Tejun Heo,
Bjorn Helgaas
Cc: Bjorn Helgaas, Manivannan Sadhasivam, Lorenzo Pieralisi,
Liam R . Howlett, Dan Williams, Lorenzo Stoakes, Michal Hocko,
Mike Rapoport, Simona Vetter, Suren Baghdasaryan, Vlastimil Babka,
linux-pci, linux-mm, driver-core
Currently, kernfs_drain_open_files() unmaps file_inode(of->file)->i_mapping
when a node with mmapped open files is removed. Since commit 636b21b50152
("PCI: Revoke mappings like devmem"), PCI resource and legacy sysfs files
swap their f_mapping to iomem_get_mapping() at open time, so their VMAs
are attached to the shared iomem address space, and the drain walks the
empty sysfs inode interval tree instead.
As a result, userspace mappings of PCI BARs survive device removal
and BAR resize, keeping stale PTEs into physical address space that
the kernel may have reassigned since.
Thus, use unmap_mapping_file() when the file's f_mapping no longer
points at its own inode mapping, so the drain zaps exactly the VMAs
created through the removed file, while unrelated mappings on the
shared address space are left intact. Files with an unmodified inode
mapping keep the existing unmap_mapping_range() behaviour, and the
driver-claim revocation through revoke_iomem() is unaffected because
the VMAs stay on the shared mapping.
This restores the teardown behaviour these files had before the
f_mapping swap was introduced. A read through a stale mapping
after removal now raises SIGBUS instead of returning stale data.
Fixes: 636b21b50152 ("PCI: Revoke mappings like devmem")
Signed-off-by: Krzysztof Wilczyński <kwilczynski@kernel.org>
---
fs/kernfs/file.c | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/fs/kernfs/file.c b/fs/kernfs/file.c
index 8e0e90c93372..8d1e4acc6716 100644
--- a/fs/kernfs/file.c
+++ b/fs/kernfs/file.c
@@ -818,7 +818,10 @@ void kernfs_drain_open_files(struct kernfs_node *kn)
struct inode *inode = file_inode(of->file);
if (of->mmapped) {
- unmap_mapping_range(inode->i_mapping, 0, 0, 1);
+ if (of->file->f_mapping != inode->i_mapping)
+ unmap_mapping_file(of->file);
+ else
+ unmap_mapping_range(inode->i_mapping, 0, 0, 1);
of->mmapped = false;
on->nr_mmapped--;
}
--
2.55.0
^ permalink raw reply related [flat|nested] 5+ messages in thread
* Re: [PATCH 1/2] mm: Add unmap_mapping_file() helper
2026-07-21 18:52 ` [PATCH 1/2] mm: Add unmap_mapping_file() helper Krzysztof Wilczyński
@ 2026-07-21 19:19 ` David Hildenbrand (Arm)
2026-07-22 4:15 ` Krzysztof Wilczyński
0 siblings, 1 reply; 5+ messages in thread
From: David Hildenbrand (Arm) @ 2026-07-21 19:19 UTC (permalink / raw)
To: Krzysztof Wilczyński, Andrew Morton, Greg Kroah-Hartman,
Tejun Heo, Bjorn Helgaas
Cc: Bjorn Helgaas, Manivannan Sadhasivam, Lorenzo Pieralisi,
Liam R . Howlett, Dan Williams, Lorenzo Stoakes, Michal Hocko,
Mike Rapoport, Simona Vetter, Suren Baghdasaryan, Vlastimil Babka,
linux-pci, linux-mm, driver-core, Christian Brauner
On 7/21/26 20:52, Krzysztof Wilczyński wrote:
> Currently, unmap_mapping_pages() and unmap_mapping_range() unmap
> a page offset window of an address_space, and teardown paths such
> as kernfs_drain_open_files() rely on them to remove the mappings
> of a file that is going away.
>
> Files with f_mapping swapped to a shared address space at open time,
> such as PCI resource files using iomem_get_mapping(), have their
> VMAs attached to the shared mapping instead of the file's own inode
> mapping, so a teardown that unmaps the inode's address space cannot
> reach them, and they stay live after the file is removed. As such,
> the only correct discriminator for these VMAs is the originating
> struct file recorded in vma->vm_file.
>
> Add unmap_mapping_file(), which walks the VMA interval tree of the
> file's f_mapping under i_mmap_lock_read() and zaps every VMA with
> vm_file set to the given file, using the same per-VMA zap teardown
> as unmap_mapping_pages().
>
> Signed-off-by: Krzysztof Wilczyński <kwilczynski@kernel.org>
> ---
> include/linux/mm.h | 2 ++
> mm/memory.c | 26 ++++++++++++++++++++++++++
> 2 files changed, 28 insertions(+)
>
> diff --git a/include/linux/mm.h b/include/linux/mm.h
> index 485df9c2dbdd..e882a03eaddd 100644
> --- a/include/linux/mm.h
> +++ b/include/linux/mm.h
> @@ -3183,6 +3183,7 @@ extern int fixup_user_fault(struct mm_struct *mm,
> bool *unlocked);
> void unmap_mapping_pages(struct address_space *mapping,
> pgoff_t start, pgoff_t nr, bool even_cows);
> +void unmap_mapping_file(struct file *file);
> void unmap_mapping_range(struct address_space *mapping,
> loff_t const holebegin, loff_t const holelen, int even_cows);
> #else
> @@ -3203,6 +3204,7 @@ static inline int fixup_user_fault(struct mm_struct *mm, unsigned long address,
> }
> static inline void unmap_mapping_pages(struct address_space *mapping,
> pgoff_t start, pgoff_t nr, bool even_cows) { }
> +static inline void unmap_mapping_file(struct file *file) { }
> static inline void unmap_mapping_range(struct address_space *mapping,
> loff_t const holebegin, loff_t const holelen, int even_cows) { }
> #endif
> diff --git a/mm/memory.c b/mm/memory.c
> index ff338c2abe92..f9cfb88ac4f7 100644
> --- a/mm/memory.c
> +++ b/mm/memory.c
> @@ -4423,6 +4423,32 @@ void unmap_mapping_pages(struct address_space *mapping, pgoff_t start,
> }
> EXPORT_SYMBOL_GPL(unmap_mapping_pages);
>
> +/**
> + * unmap_mapping_file() - Unmap all mmaps of an open file.
> + * @file: The file to unmap.
We are unmapping pages/folios, not really mmaps.
"Unmap folio from all mmaps of a file." ?
> + *
> + * Unmap every VMA with vm_file set to @file, regardless of the address
Again, we are not unmapping VMAs, we are unmapping pages/folios within all VMAs
that map the file.
> + * space it is attached to. This also covers files with f_mapping
> + * swapped to a different address space at open time, since such VMAs
> + * cannot be found through the file's own inode mapping.
God this is confusing :)
Sorry for the stupid question, but why can't we call
unmap_mapping_range(file->f_mapping, 0, 0, 1);
What is a bit annoying is that you now do in the caller:
if (of->file->f_mapping != inode->i_mapping)
unmap_mapping_file(of->file);
else
unmap_mapping_range(inode->i_mapping, 0, 0, 1);
Shouldn't we just have one function that takes care of that internally ... so
it's harder to get wrong?
--
Cheers,
David
^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: [PATCH 1/2] mm: Add unmap_mapping_file() helper
2026-07-21 19:19 ` David Hildenbrand (Arm)
@ 2026-07-22 4:15 ` Krzysztof Wilczyński
0 siblings, 0 replies; 5+ messages in thread
From: Krzysztof Wilczyński @ 2026-07-22 4:15 UTC (permalink / raw)
To: David Hildenbrand (Arm)
Cc: Andrew Morton, Greg Kroah-Hartman, Tejun Heo, Bjorn Helgaas,
Bjorn Helgaas, Manivannan Sadhasivam, Lorenzo Pieralisi,
Liam R . Howlett, Dan Williams, Lorenzo Stoakes, Michal Hocko,
Mike Rapoport, Simona Vetter, Suren Baghdasaryan, Vlastimil Babka,
linux-pci, linux-mm, driver-core, Christian Brauner
Hello,
> > + * unmap_mapping_file() - Unmap all mmaps of an open file.
> > + * @file: The file to unmap.
>
> We are unmapping pages/folios, not really mmaps.
>
> "Unmap folio from all mmaps of a file." ?
Got it. I will make sure that v2 has the wording improved.
> > + * space it is attached to. This also covers files with f_mapping
> > + * swapped to a different address space at open time, since such VMAs
> > + * cannot be found through the file's own inode mapping.
>
> God this is confusing :)
Sorry about that! I am not very familiar with the inner workings of
mm/, so please bear with me here a little. :)
> Sorry for the stupid question, but why can't we call
>
> unmap_mapping_range(file->f_mapping, 0, 0, 1);
Since commit 636b21b50152 ("PCI: Revoke mappings like devmem") the PCI
resource and legacy files swap their f_mapping to the shared iomem
address space returned by iomem_get_mapping(), so that revoke_iomem()
can zap existing mappings when a driver claims a region.
That address space holds the VMAs of every resourceN file of every PCI
device, the /proc/bus/pci mappings, and every /dev/mem mapping in the
system.
So, calling unmap_mapping_range(file->f_mapping, 0, 0, 1) from
kernfs_drain_open_files() would then zap all of them on any
single device removal as a side effect. This is not ideal.
I have tried exactly that, to be sure: mmap resource0 of two different
devices, then remove one of them. The mapping of the other device was
torn down as well, and the next access through it got SIGBUS, even
though that device was alive and bound.
Hence the new helper, especially since kernfs does not know which
physical range belongs to the file being removed. Only the PCI side
does. The struct file in vma->vm_file was the only thing I could
think of to use as a filter.
> What is a bit annoying is that you now do in the caller:
>
> if (of->file->f_mapping != inode->i_mapping)
> unmap_mapping_file(of->file);
> else
> unmap_mapping_range(inode->i_mapping, 0, 0, 1);
>
> Shouldn't we just have one function that takes care of that internally ... so
> it's harder to get wrong?
The idea was to introduce as little change under mm/ as possible, keeping
things inside kernfs, which is the de facto consumer of the API from mm/,
so I wanted to make it obvious at the call site what needs to be done, so
to speak.
I will change it, such that in v2, kernfs will simply call
unmap_mapping_file(), and the helper will take care of both cases
internally.
Thank you for the review!
Krzysztof
^ permalink raw reply [flat|nested] 5+ messages in thread
end of thread, other threads:[~2026-07-22 4:15 UTC | newest]
Thread overview: 5+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-07-21 18:52 [PATCH 0/2] mm,kernfs: Fix stale userspace mappings after sysfs file removal Krzysztof Wilczyński
2026-07-21 18:52 ` [PATCH 1/2] mm: Add unmap_mapping_file() helper Krzysztof Wilczyński
2026-07-21 19:19 ` David Hildenbrand (Arm)
2026-07-22 4:15 ` Krzysztof Wilczyński
2026-07-21 18:52 ` [PATCH 2/2] kernfs: Unmap mmaps of removed files via file->f_mapping Krzysztof Wilczyński
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox