* + mm-vma-fix-mmap_prepare-file-handling-remove-file_doesnt_need_get.patch added to mm-unstable branch
@ 2026-10-10 22:01 Andrew Morton
0 siblings, 0 replies; 5+ messages in thread
From: Andrew Morton @ 2026-10-10 22:01 UTC (permalink / raw)
To: mm-commits, ljs, akpm
The patch titled
Subject: mm/vma: fix mmap_prepare file handling, remove file_doesnt_need_get
has been added to the -mm mm-unstable branch. Its filename is
mm-vma-fix-mmap_prepare-file-handling-remove-file_doesnt_need_get.patch
This patch will shortly appear at
https://git.kernel.org/pub/scm/linux/kernel/git/akpm/25-new.git/tree/patches/mm-vma-fix-mmap_prepare-file-handling-remove-file_doesnt_need_get.patch
This patch will later appear in the mm-unstable branch at
git://git.kernel.org/pub/scm/linux/kernel/git/akpm/mm
Before you just go and hit "reply", please:
a) Consider who else should be cc'ed
b) Prefer to cc a suitable mailing list as well
c) Ideally: find the original patch on the mailing list and do a
reply-to-all to that, adding suitable additional cc's
*** Remember to use Documentation/process/submit-checklist.rst when testing your code ***
The -mm tree is included into linux-next via various
branches at git://git.kernel.org/pub/scm/linux/kernel/git/akpm/mm
and is updated there most days
------------------------------------------------------
From: "Lorenzo Stoakes (ARM)" <ljs@kernel.org>
Subject: mm/vma: fix mmap_prepare file handling, remove file_doesnt_need_get
Date: Fri, 09 Oct 2026 19:03:55 +0100
Patch series "mm: make VMA flag semantics explicit, eliminate VM_SPECIAL",
v5.
The VM_SPECIAL / VMA_SPECIAL_FLAGS mask conflates several unrelated
properties:
* Is this managed by core mm, or by a driver mapping MMIO, kernel-allocated
pages, or even ordinary pages itself?
* Can it be expanded or merged?
* Is this a 'weird' case like mlock where migration might race and we
'have' to set invalid flags to notify?
* Is it another 'weird' case where we just want to stop GUP from touching
it?
Driver writers have often been confused about this, and who can blame them?
It also interacts badly with the eternal edgecase known as hugetlb - which
sets VMA_DONTEXPAND_BIT but doesn't also want to be treated like a
'special' flag.
Another issue is that we cannot make sensible assumptions about flag
use. It's not possible to assume VMA_IO_BIT means iommu because drivers
abuse it and mlock abuses it.
Special is also an overloaded term in mm. VDSO and VVAR mappings are also
called 'special' but they're special in a... special way.
Sometimes things are called special that are a subset of
VMA_SPECIAL_FLAGS (VMA_PFNMAP_BIT and VMA_MIXEDMAP_BIT for instance when it
comes to zapping or vm_normal_folio()).
There's a specific kind of special for THP too, which considers
PFN map, mixed map 'special' but DAX not.
It's all rather a mess.
This series brings some order to things by both limiting what drivers can
do with VMA flags and switching to using predicates that describe
behaviour, not arbitrary flags.
It establishes the invariant that mm-managed mappings may neither set
VMA_IO_BIT nor clear VMA_MAYWRITE_BIT in an mmap hook, enforcing this by
validating VMA state after every mmap and mmap_prepare hook.
It updates usbmon and sg to mmap_prepare in order to do so, adding a new
mmap action for mapping discontiguous kernel pages, and has hfi1 and the
ALSA PCM status page map their pages eagerly instead.
It also establishes the invariant that VMA_MIXEDMAP_BIT be set when mapping
kernel memory, something that is usually the case but happens not to be for
some users - specifically defio, cmt_speech, uprobes and the bpf arena, all
of which are updated to do the right thing.
It replaces VM_SPECIAL and arbitrary flag tests with predicates that say
what is actually being tested:
vma_is_mm_managed() Are the VMA's contents established and managed
by core mm, rather than a driver or other kernel
component?
vma_is_mm_backed() Is the VMA backed by memory core mm itself
faults in and keeps, rather than a driver or
device?
vma_is_fixed_mapping() Is the VMA not permitted to be expanded or
merged?
vma_can_merge() Can the VMA be merged with a compatible
neighbour?
Remaining raw VMA_IO_BIT, VMA_PFNMAP_BIT and VMA_MIXEDMAP_BIT tests
scattered across mm are converted to predicates where they express one, and
left as explicit flag tests where they do not.
And also the opportunity is taken to eliminate THP's vma_is_special_huge()
which was an existing source of confusion.
This patch (of 38):
The map->file_doesnt_need_get flag is confusing and the existing
implementation has holes.
Drivers are permitted to change the owning file of a mapping. If they do
so, they are required to take a reference on that file.
The mmap() operation which ultimately invokes __mmap_region() is guaranteed
to drop the refcount for the original file the mapping was made under, but
this is not true for the replaced file.
This has been addressed so far by tracking map->file_doesnt_need_get, which
is rather poorly named and unfortunately fails to correctly track whether
or not an additional put were needed in a number of cases.
Make life easier by removing this flag, and instead drop the reference for
both mmap_prepare and the deprecated mmap callback in a new function
map_put_vm_file().
Track whether this needs to be done by aligning mmap_state with
vm_area_desc and store the original file in the map->file field, keeping
the updated file in map->vm_file.
In order to have the same behaviour for both types of hooks, only drop the
reference __mmap_new_file_vma() itself took in its error path, deferring
the replaced file's reference to map_put_vm_file().
To make this work correctly, map->vm_file has to be updated before any
error handling, so update __mmap_new_file_vma() and call_mmap_prepare() to
set this field first.
Also when mmap_prepare() changes the file and is then merged, the reference
count also must be decremented, so update the logic to call
map_put_vm_file() in this case too.
Also update __compat_vma_mmap() to manually perform this step for stacked
file systems using the compatibility layer, and update
compat_set_vma_from_desc() to replace vma_set_file() with a correct
refcount/file update.
No in-tree driver is impacted by the incorrect implementation of this
currently (no driver that does this is mergeable for one), so this does not
need to be a fix.
Link: https://lore.kernel.org/20261009-b4-mmap-prepare-vma-flag-sanify-v5-0-923b70125b1e@kernel.org
Link: https://lore.kernel.org/20261009-b4-mmap-prepare-vma-flag-sanify-v5-1-923b70125b1e@kernel.org
Signed-off-by: Lorenzo Stoakes (ARM) <ljs@kernel.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Reviewed-by: Suren Baghdasaryan <surenb@google.com>
Cc: Liam R. Howlett <liam@infradead.org>
Cc: Vlastimil Babka <vbabka@kernel.org>
Cc: Jann Horn <jannh@google.com>
Cc: Pedro Falcato <pfalcato@suse.de>
Cc: David Hildenbrand <david@kernel.org>
Cc: Mike Rapoport <rppt@kernel.org>
Cc: Michal Hocko <mhocko@suse.com>
Cc: Jonathan Corbet <corbet@lwn.net>
Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Cc: Dennis Dalessandro <dennis.dalessandro@cornelisnetworks.com>
Cc: Jason Gunthorpe <jgg@ziepe.ca>
Cc: Leon Romanovsky <leon@kernel.org>
Cc: Paul Moore <paul@paul-moore.com>
Cc: Stephen Smalley <stephen.smalley.work@gmail.com>
Cc: Jaroslav Kysela <perex@perex.cz>
Cc: Takashi Iwai <tiwai@suse.com>
Cc: Alexei Starovoitov <ast@kernel.org>
Cc: Daniel Borkmann <daniel@iogearbox.net>
Cc: Andrii Nakryiko <andrii@kernel.org>
Cc: Eduard Zingerman <eddyz87@gmail.com>
Cc: Kumar Kartikeya Dwivedi <memxor@gmail.com>
Cc: Zi Yan <ziy@nvidia.com>
Cc: Baolin Wang <baolin.wang@linux.alibaba.com>
Cc: Nico Pache <nico.pache@linux.dev>
Cc: Ryan Roberts <ryan.roberts@arm.com>
Cc: Dev Jain <dev.jain@arm.com>
Cc: Barry Song <baohua@kernel.org>
Cc: Lance Yang <lance.yang@linux.dev>
Cc: Usama Arif <usama.arif@linux.dev>
Cc: Kiryl Shutsemau <kas@kernel.org>
Cc: Doug Gilbert <dgilbert@interlog.com>
Cc: James Bottomley <james.bottomley@hansenpartnership.com>
Cc: Martin K. Petersen <mkp@kernel.org>
Cc: Simona Vetter <simona@ffwll.ch>
Cc: Helge Deller <deller@gmx.de>
Cc: Sebastian Reichel <sre@kernel.org>
Cc: John Hubbard <jhubbard@nvidia.com>
Cc: Peter Xu <peterx@redhat.com>
Cc: Masami Hiramatsu <mhiramat@kernel.org>
Cc: Oleg Nesterov <oleg@redhat.com>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Thomas Gleixner <tglx@kernel.org>
Cc: Ingo Molnar <mingo@redhat.com>
Cc: Borislav Petkov <bp@alien8.de>
Cc: Dave Hansen <dave.hansen@linux.intel.com>
Cc: Arnaldo Carvalho de Melo <acme@kernel.org>
Cc: Namhyung Kim <namhyung@kernel.org>
Cc: Mark Rutland <mark.rutland@arm.com>
Cc: Rik van Riel <riel@surriel.com>
Cc: Harry Yoo <harry@kernel.org>
Cc: Juri Lelli <juri.lelli@redhat.com>
Cc: Vincent Guittot <vincent.guittot@linaro.org>
Cc: Maarten Lankhorst <maarten.lankhorst@linux.intel.com>
Cc: Maxime Ripard <mripard@kernel.org>
Cc: Thomas Zimmermann <tzimmermann@suse.de>
Cc: David Airlie <airlied@gmail.com>
Cc: Will Deacon <will@kernel.org>
Cc: Aneesh Kumar K.V <aneesh.kumar@kernel.org>
Cc: Nicholas Piggin <npiggin@gmail.com>
Cc: Arnd Bergmann <arnd@arndb.de>
Cc: Muchun Song <muchun.song@linux.dev>
Cc: Oscar Salvador <osalvador@suse.de>
Cc: Matthew Wilcox (Oracle) <willy@infradead.org>
Cc: Jan Kara <jack@suse.cz>
Cc: Marc Zyngier <maz@kernel.org>
Cc: Oliver Upton <oupton@kernel.org>
Cc: Catalin Marinas <catalin.marinas@arm.com>
Cc: Madhavan Srinivasan <maddy@linux.ibm.com>
Cc: Anup Patel <anup@brainfault.org>
Cc: Paul Walmsley <pjw@kernel.org>
Cc: Palmer Dabbelt <palmer@dabbelt.com>
Cc: Albert Ou <aou@eecs.berkeley.edu>
Cc: Christian Borntraeger <borntraeger@linux.ibm.com>
Cc: Janosch Frank <frankja@linux.ibm.com>
Cc: Claudio Imbrenda <imbrenda@linux.ibm.com>
Cc: Alexander Gordeev <agordeev@linux.ibm.com>
Cc: Gerald Schaefer <gerald.schaefer@linux.ibm.com>
Cc: Heiko Carstens <hca@linux.ibm.com>
Cc: Vasily Gorbik <gor@linux.ibm.com>
Cc: David S. Miller <davem@davemloft.net>
Cc: Andreas Larsson <andreas@gaisler.com>
Cc: Al Viro <viro@zeniv.linux.org.uk>
Cc: Christian Brauner <brauner@kernel.org>
Cc: Matthew Brost <matthew.brost@intel.com>
Cc: Joshua Hahn <joshua.hahnjy@gmail.com>
Cc: Rakie Kim <rakie.kim@sk.com>
Cc: Byungchul Park <byungchul@sk.com>
Cc: Gregory Price <gourry@gourry.net>
Cc: Ying Huang <ying.huang@linux.alibaba.com>
Cc: Alistair Popple <apopple@nvidia.com>
Cc: Chris Li <chrisl@kernel.org>
Cc: Kairui Song <kasong@tencent.com>
Cc: Kemeng Shi <shikemeng@huaweicloud.com>
Cc: Nhat Pham <nphamcs@gmail.com>
Cc: Baoquan He <baoquan.he@linux.dev>
Cc: Youngjun Park <youngjun.park@lge.com>
Cc: Johannes Weiner <hannes@cmpxchg.org>
Cc: Qi Zheng <qi.zheng@linux.dev>
Cc: Shakeel Butt <shakeel.butt@linux.dev>
Cc: Axel Rasmussen <axelrasmussen@google.com>
Cc: Yuanchu Xie <yuanchu@google.com>
Cc: Wei Xu <weixugc@google.com>
Cc: Chengming Zhou <chengming.zhou@linux.dev>
Cc: Michal Hocko <mhocko@kernel.org>
Cc: Miklos Szeredi <miklos@szeredi.hu>
Cc: Xu Xin <xu.xin@linux.dev>
Cc: Ihor Solodrai <ihor.solodrai@linux.dev>
Cc: Bert Karwatzki <spasswolf@web.de>
---
mm/internal.h | 1
mm/util.c | 5 ++
mm/vma.c | 81 +++++++++++++++++++++++++++++-------------------
mm/vma.h | 6 ++-
4 files changed, 58 insertions(+), 35 deletions(-)
--- a/mm/internal.h~mm-vma-fix-mmap_prepare-file-handling-remove-file_doesnt_need_get
+++ a/mm/internal.h
@@ -7,6 +7,7 @@
#ifndef __MM_INTERNAL_H
#define __MM_INTERNAL_H
+#include <linux/file.h>
#include <linux/fs.h>
#include <linux/khugepaged.h>
#include <linux/mm.h>
--- a/mm/util.c~mm-vma-fix-mmap_prepare-file-handling-remove-file_doesnt_need_get
+++ a/mm/util.c
@@ -1228,8 +1228,11 @@ int __compat_vma_mmap(struct vm_area_des
/* Perform any preparatory tasks for mmap action. */
err = mmap_action_prepare(desc);
- if (err)
+ if (err) {
+ if (desc->vm_file != vma->vm_file)
+ fput(desc->vm_file);
return err;
+ }
/* Update the VMA from the descriptor. */
compat_set_vma_from_desc(vma, desc);
/* Complete any specified mmap actions. */
--- a/mm/vma.c~mm-vma-fix-mmap_prepare-file-handling-remove-file_doesnt_need_get
+++ a/mm/vma.c
@@ -24,7 +24,8 @@ struct mmap_state {
vm_flags_t vm_flags;
vma_flags_t vma_flags;
};
- struct file *file;
+ struct file *file; /* mmap()-specified file. */
+ struct file *vm_file; /* May be updated by mmap_prepare. */
pgprot_t page_prot;
/* User-defined fields, perhaps updated by .mmap_prepare(). */
@@ -43,8 +44,6 @@ struct mmap_state {
/* Determine if we can check KSM flags early in mmap() logic. */
bool check_ksm_early :1;
- /* If .mmap_prepare changed the file, we don't need to pin. */
- bool file_doesnt_need_get :1;
};
#define MMAP_STATE(name, mm_, vmi_, addr_, len_, pgoff_, anon_pgoff_, vma_flags_, file_) \
@@ -58,6 +57,7 @@ struct mmap_state {
.pglen = PHYS_PFN(len_), \
.vma_flags = vma_flags_, \
.file = file_, \
+ .vm_file = file_, \
.page_prot = vma_flags_to_page_prot(vma_flags_), \
}
@@ -70,7 +70,7 @@ struct mmap_state {
.vma_flags = (map_)->vma_flags, \
.pgoff = (map_)->pgoff, \
.anon_pgoff = (map_)->anon_pgoff, \
- .file = (map_)->file, \
+ .file = (map_)->vm_file, \
.prev = (map_)->prev, \
.middle = vma_, \
.next = (vma_) ? NULL : (map_)->next, \
@@ -2462,7 +2462,7 @@ void mm_drop_all_locks(struct mm_struct
*/
static bool accountable_mapping(struct mmap_state *map)
{
- const struct file *file = map->file;
+ const struct file *file = map->vm_file;
/*
* hugetlb has its own accounting separate from the core VM
@@ -2511,7 +2511,7 @@ static void vms_abort_munmap_vmas(struct
static void update_ksm_flags(struct mmap_state *map)
{
- map->vma_flags = ksm_vma_flags(map->mm, map->file, map->vma_flags);
+ map->vma_flags = ksm_vma_flags(map->mm, map->vm_file, map->vma_flags);
}
static void set_desc_from_map(struct vm_area_desc *desc,
@@ -2521,7 +2521,7 @@ static void set_desc_from_map(struct vm_
desc->end = map->end;
desc->pgoff = map->pgoff;
- desc->vm_file = map->file;
+ desc->vm_file = map->vm_file;
desc->vma_flags = map->vma_flags;
desc->page_prot = map->page_prot;
}
@@ -2601,6 +2601,10 @@ static int __mmap_setup(struct mmap_stat
return 0;
}
+static bool map_same_file(struct mmap_state *map)
+{
+ return map->vm_file == map->file;
+}
static int __mmap_new_file_vma(struct mmap_state *map,
struct vm_area_struct *vma)
@@ -2608,20 +2612,23 @@ static int __mmap_new_file_vma(struct mm
struct vma_iterator *vmi = map->vmi;
int error;
- vma->vm_file = map->file;
- if (!map->file_doesnt_need_get)
- get_file(map->file);
+ vma->vm_file = map->vm_file;
+ if (map_same_file(map))
+ get_file(map->vm_file);
- if (!map->file->f_op->mmap)
+ if (!map->vm_file->f_op->mmap)
return 0;
error = mmap_file(vma->vm_file, vma);
+ map->vm_file = vma->vm_file;
+
if (error) {
UNMAP_STATE(unmap, vmi, vma, vma->vm_start, vma->vm_end,
map->prev, map->next);
- fput(vma->vm_file);
- vma->vm_file = NULL;
+ if (map_same_file(map))
+ fput(map->vm_file);
+ vma->vm_file = NULL;
vma_iter_set(vmi, vma->vm_end);
/* Undo any partial mapping done by a device driver. */
unmap_region(&unmap);
@@ -2638,7 +2645,6 @@ static int __mmap_new_file_vma(struct mm
!vma_flags_test(&map->vma_flags, VMA_MAYWRITE_BIT) &&
vma_test(vma, VMA_MAYWRITE_BIT));
- map->file = vma->vm_file;
map->vma_flags = vma->flags;
return 0;
@@ -2646,7 +2652,7 @@ static int __mmap_new_file_vma(struct mm
static void map_set_anon(struct mmap_state *map)
{
- map->file = NULL;
+ map->vm_file = NULL;
map->vm_ops = NULL;
map->pgoff = map->addr >> PAGE_SHIFT;
}
@@ -2658,7 +2664,7 @@ static bool map_is_private(const struct
static bool map_is_anon(const struct mmap_state *map)
{
- return map_is_private(map) && !map->file;
+ return map_is_private(map) && !map->vm_file;
}
/*
@@ -2703,7 +2709,7 @@ static int __mmap_new_vma(struct mmap_st
}
/* Invoke callbacks. */
- if (map->file)
+ if (map->vm_file)
error = __mmap_new_file_vma(map, vma);
else if (!is_anon)
error = shmem_zero_setup(vma);
@@ -2812,10 +2818,14 @@ static int call_mmap_prepare(struct mmap
int err;
/* Invoke the hook. */
- err = vfs_mmap_prepare(map->file, desc);
+ err = vfs_mmap_prepare(map->vm_file, desc);
if (err)
return err;
+ /* Update first so file refcount tracked correctly. */
+ if (desc->vm_file != map->vm_file)
+ map->vm_file = desc->vm_file;
+
/* It's invalid for mmap_prepare hooks to clear vm_ops. */
if (!desc->vm_ops)
return -EINVAL;
@@ -2826,10 +2836,6 @@ static int call_mmap_prepare(struct mmap
/* Update fields permitted to be changed. */
map->pgoff = desc->pgoff;
- if (desc->vm_file != map->file) {
- map->file_doesnt_need_get = true;
- map->file = desc->vm_file;
- }
map->vma_flags = desc->vma_flags;
map->page_prot = desc->page_prot;
/* User-defined fields. */
@@ -2841,7 +2847,7 @@ static int call_mmap_prepare(struct mmap
* anonymous mappings. Rather than allowing these mappings to be odd
* outliers, simply make them truly anonymous.
*/
- if (map_is_private(map) && file_is_dev_zero(map->file))
+ if (map_is_private(map) && file_is_dev_zero(map->vm_file))
map_set_anon(map);
return 0;
@@ -2860,7 +2866,7 @@ static void set_vma_user_defined_fields(
*/
static bool can_set_ksm_flags_early(struct mmap_state *map)
{
- struct file *file = map->file;
+ struct file *file = map->vm_file;
/* Anonymous mappings have no driver which can change them. */
if (!file)
@@ -2883,6 +2889,20 @@ static bool can_set_ksm_flags_early(stru
return false;
}
+static void map_put_vm_file(struct mmap_state *map)
+{
+ /*
+ * An error occurred or the VMA was merged.
+ *
+ * If the file was changed by the driver (which is required to increment
+ * the replacement file's reference count), drop its reference count.
+ *
+ * On error, the caller always drops the original file regardless.
+ */
+ if (map->vm_file && !map_same_file(map))
+ fput(map->vm_file);
+}
+
static unsigned long __mmap_region(struct file *file, unsigned long addr,
unsigned long len, vma_flags_t vma_flags,
unsigned long pgoff, struct list_head *uf)
@@ -2937,7 +2957,10 @@ static unsigned long __mmap_region(struc
__mmap_complete(&map, vma);
- if (have_mmap_prepare && allocated_new) {
+ if (!allocated_new) {
+ /* Merged, so need to drop refcount. */
+ map_put_vm_file(&map);
+ } else if (have_mmap_prepare) {
error = mmap_action_complete(vma, &desc.action,
/*is_compat=*/false);
if (error)
@@ -2951,13 +2974,7 @@ unacct_error:
if (map.charged)
vm_unacct_memory(map.charged);
abort_munmap:
- /*
- * This indicates that .mmap_prepare has set a new file, differing from
- * desc->vm_file. But since we're aborting the operation, only the
- * original file will be cleaned up. Ensure we clean up both.
- */
- if (map.file_doesnt_need_get)
- fput(map.file);
+ map_put_vm_file(&map);
vms_abort_munmap_vmas(&map.vms, &map.mas_detach);
return error;
}
--- a/mm/vma.h~mm-vma-fix-mmap_prepare-file-handling-remove-file_doesnt_need_get
+++ a/mm/vma.h
@@ -394,8 +394,10 @@ static inline void compat_set_vma_from_d
/* Mutable fields. Populated with initial state. */
vma_set_pgoff(vma, desc->pgoff);
- if (desc->vm_file != vma->vm_file)
- vma_set_file(vma, desc->vm_file);
+ if (desc->vm_file != vma->vm_file) {
+ fput(vma->vm_file);
+ vma->vm_file = desc->vm_file;
+ }
vma->flags = desc->vma_flags;
vma->vm_page_prot = desc->page_prot;
_
Patches currently in -mm which might be from ljs@kernel.org are
mm-vma-fix-mmap_prepare-file-handling-remove-file_doesnt_need_get.patch
mm-vma-introduce-and-use-vma_can_merge.patch
mm-consistently-validate-vma-state-after-mmap-hooks.patch
mm-vma-ensure-mmap_prepare-doesnt-set-actions-on-a-mergeable-vma.patch
mm-make-map_kernel_pages_-internal-and-unexported.patch
mm-vma-tidy-up-map-kernel-pages-enum-values.patch
mm-add-mmap-action-for-discontiguous-kernel-page-mapping.patch
docs-filesystems-update-mmap_prepare-docs-for-discontig-kernel-pgs.patch
drivers-usb-mon-update-to-use-mmap_prepare-map-kernel-pages.patch
infiniband-update-hfi1-to-use-remap_vmalloc_range.patch
selinux-reject-writable-opens-of-policy-file-drop-mmap-shared-write-check.patch
alsa-pcm-use-vm_insert_page-to-map-pcm-status-page.patch
bpf-arena-mark-arena_map_mmap-mappings-vm_mixedmap.patch
mm-vma-add-vma_is_mm_managed-predicates.patch
mm-vma-only-allow-mmap-to-clear-vma_maywrite_bit-if-not-mm-managed.patch
mm-vma-add-and-use-vma__is_fixed_mapping.patch
scsi-sg-convert-mmap-hook-to-mmap_prepare-and-rework.patch
fbdev-defio-assert-fbinfo_virtfb-drop-vm_io-add-vm_mixedmap.patch
hsi-cmt_speech-convert-mmap-hook-to-mmap_prepare-refactor.patch
mm-gup-error-out-early-on-vma_mayread_bit-vmas.patch
uprobes-remove-vm_io-set-vm_mixedmap-for-mapped-kernel-pages.patch
mm-mlock-clear-vma_locked_mask-over-mmap-callback.patch
mm-mlock-eliminate-weird-vma_io_bit-abuse-and-simplify.patch
mm-vma-enforce-that-mm-managed-mappings-may-not-set-vma_io_bit.patch
mm-remove-vma_io_bit-check-in-vma_is_mm_managed.patch
mm-remove-hugetlb_inlineh.patch
mm-rename-is_vm_hugetlb_page-to-vma_is_hugetlb.patch
mm-drop-some-redundant-checks-around-hugetlb-vmas.patch
mm-madvise-update-is_valid_guard_vma-to-use-vma_can_merge.patch
mm-vma-introduce-vma_is_mm_backed.patch
mm-uffd-use-predicates-for-userfaultfd-checks.patch
mm-madvise-use-predicates-for-madvise-madv_dofork.patch
mm-eliminate-vma_special_flags-usage-when-hugetlb-explicitly-tested.patch
mm-eliminate-vma_special_flags-check-in-lru_gen_look_around.patch
mm-avoid-use-of-vma_special_flags-in-migrate_vma_setup.patch
mm-eliminate-vm_special-vma_special_flags.patch
fuse-dax-do-not-set-vm_mixedmap.patch
mm-huge_memory-remove-vma_is_special_huge.patch
mm-vma-const-ify-vma_assert_stabilised-and-associated-functions.patch
mm-implement-and-use-vma_has_anon_rmap-silence-kcsan.patch
mm-update-comments-to-refer-to-anon-rmap-rather-than-anon_vma.patch
mm-vma-dont-remove-vma-from-rmap-if-pgoff-unchanged.patch
^ permalink raw reply [flat|nested] 5+ messages in thread* + mm-vma-fix-mmap_prepare-file-handling-remove-file_doesnt_need_get.patch added to mm-unstable branch
@ 2026-10-13 18:51 Andrew Morton
0 siblings, 0 replies; 5+ messages in thread
From: Andrew Morton @ 2026-10-13 18:51 UTC (permalink / raw)
To: mm-commits, ljs, akpm
The patch titled
Subject: mm/vma: fix mmap_prepare file handling, remove file_doesnt_need_get
has been added to the -mm mm-unstable branch. Its filename is
mm-vma-fix-mmap_prepare-file-handling-remove-file_doesnt_need_get.patch
This patch will shortly appear at
https://git.kernel.org/pub/scm/linux/kernel/git/akpm/25-new.git/tree/patches/mm-vma-fix-mmap_prepare-file-handling-remove-file_doesnt_need_get.patch
This patch will later appear in the mm-unstable branch at
git://git.kernel.org/pub/scm/linux/kernel/git/akpm/mm
Before you just go and hit "reply", please:
a) Consider who else should be cc'ed
b) Prefer to cc a suitable mailing list as well
c) Ideally: find the original patch on the mailing list and do a
reply-to-all to that, adding suitable additional cc's
*** Remember to use Documentation/process/submit-checklist.rst when testing your code ***
The -mm tree is included into linux-next via various
branches at git://git.kernel.org/pub/scm/linux/kernel/git/akpm/mm
and is updated there most days
------------------------------------------------------
From: "Lorenzo Stoakes (ARM)" <ljs@kernel.org>
Subject: mm/vma: fix mmap_prepare file handling, remove file_doesnt_need_get
Date: Tue, 13 Oct 2026 16:04:41 +0100
Patch series "mm: make VMA flag semantics explicit, eliminate VM_SPECIAL",
v6.
The VM_SPECIAL / VMA_SPECIAL_FLAGS mask conflates several unrelated
properties:
* Is this managed by core mm, or by a driver mapping MMIO, kernel-allocated
pages, or even ordinary pages itself?
* Can it be expanded or merged?
* Is this a 'weird' case like mlock where migration might race and we
'have' to set invalid flags to notify?
* Is it another 'weird' case where we just want to stop GUP from touching
it?
Driver writers have often been confused about this, and who can blame them?
It also interacts badly with the eternal edgecase known as hugetlb - which
sets VMA_DONTEXPAND_BIT but doesn't also want to be treated like a
'special' flag.
Another issue is that we cannot make sensible assumptions about flag
use. It's not possible to assume VMA_IO_BIT means iommu because drivers
abuse it and mlock abuses it.
Special is also an overloaded term in mm. VDSO and VVAR mappings are also
called 'special' but they're special in a... special way.
Sometimes things are called special that are a subset of
VMA_SPECIAL_FLAGS (VMA_PFNMAP_BIT and VMA_MIXEDMAP_BIT for instance when it
comes to zapping or vm_normal_folio()).
There's a specific kind of special for THP too, which considers
PFN map, mixed map 'special' but DAX not.
It's all rather a mess.
This series brings some order to things by both limiting what drivers can
do with VMA flags and switching to using predicates that describe
behaviour, not arbitrary flags.
It establishes the invariant that mm-managed mappings may neither set
VMA_IO_BIT nor clear VMA_MAYWRITE_BIT in an mmap hook, enforcing this by
validating VMA state after every mmap and mmap_prepare hook.
It updates usbmon and sg to mmap_prepare in order to do so, adding a new
mmap action for mapping discontiguous kernel pages, and has hfi1 and the
ALSA PCM status page map their pages eagerly instead.
It also establishes the invariant that VMA_MIXEDMAP_BIT be set when mapping
kernel memory, something that is usually the case but happens not to be for
some users - specifically defio, cmt_speech, uprobes and the bpf arena, all
of which are updated to do the right thing.
It replaces VM_SPECIAL and arbitrary flag tests with predicates that say
what is actually being tested:
vma_is_mm_managed() Are the VMA's contents established and managed
by core mm, rather than a driver or other kernel
component?
vma_is_mm_backed() Is the VMA backed by memory core mm itself
faults in, rather than a driver or device?
vma_is_fixed_mapping() Is the VMA not permitted to be expanded or
merged?
vma_can_merge() Can the VMA be merged with a compatible
neighbour?
Remaining raw VMA_IO_BIT, VMA_PFNMAP_BIT and VMA_MIXEDMAP_BIT tests
scattered across mm are converted to predicates where they express one, and
left as explicit flag tests where they do not.
And also the opportunity is taken to eliminate THP's vma_is_special_huge()
which was an existing source of confusion.
This patch (of 39):
The map->file_doesnt_need_get flag is confusing and the existing
implementation has holes.
Drivers are permitted to change the owning file of a mapping. If they do
so, they are required to take a reference on that file.
The mmap() operation which ultimately invokes __mmap_region() is guaranteed
to drop the refcount for the original file the mapping was made under, but
this is not true for the replaced file.
This has been addressed so far by tracking map->file_doesnt_need_get, which
is rather poorly named and unfortunately fails to correctly track whether
or not an additional put were needed in a number of cases.
Make life easier by removing this flag, and instead drop the reference for
both mmap_prepare and the deprecated mmap callback in a new function
map_put_vm_file().
Track whether this needs to be done by aligning mmap_state with
vm_area_desc and store the original file in the map->file field, keeping
the updated file in map->vm_file.
In order to have the same behaviour for both types of hooks, only drop the
reference __mmap_new_file_vma() itself took in its error path, deferring
the replaced file's reference to map_put_vm_file().
To make this work correctly, map->vm_file has to be updated before any
error handling, so update __mmap_new_file_vma() and call_mmap_prepare() to
set this field first.
Also when mmap_prepare() changes the file and is then merged, the reference
count also must be decremented, so update the logic to call
map_put_vm_file() in this case too.
Also update __compat_vma_mmap() to manually perform this step for stacked
file systems using the compatibility layer, and update
compat_set_vma_from_desc() to replace vma_set_file() with a correct
refcount/file update.
No in-tree driver is impacted by the incorrect implementation of this
currently (no driver that does this is mergeable for one), so this does not
need to be a fix.
Link: https://lore.kernel.org/20261013-b4-mmap-prepare-vma-flag-sanify-v6-0-1aa3c9b139f9@kernel.org
Link: https://lore.kernel.org/20261013-b4-mmap-prepare-vma-flag-sanify-v6-1-1aa3c9b139f9@kernel.org
Signed-off-by: Lorenzo Stoakes (ARM) <ljs@kernel.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Reviewed-by: Suren Baghdasaryan <surenb@google.com>
Cc: Liam R. Howlett <liam@infradead.org>
Cc: Vlastimil Babka <vbabka@kernel.org>
Cc: Jann Horn <jannh@google.com>
Cc: Pedro Falcato <pfalcato@suse.de>
Cc: David Hildenbrand <david@kernel.org>
Cc: Mike Rapoport <rppt@kernel.org>
Cc: Michal Hocko <mhocko@suse.com>
Cc: Jonathan Corbet <corbet@lwn.net>
Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Cc: Dennis Dalessandro <dennis.dalessandro@cornelisnetworks.com>
Cc: Jason Gunthorpe <jgg@ziepe.ca>
Cc: Leon Romanovsky <leon@kernel.org>
Cc: Paul Moore <paul@paul-moore.com>
Cc: Stephen Smalley <stephen.smalley.work@gmail.com>
Cc: Jaroslav Kysela <perex@perex.cz>
Cc: Takashi Iwai <tiwai@suse.com>
Cc: Alexei Starovoitov <ast@kernel.org>
Cc: Daniel Borkmann <daniel@iogearbox.net>
Cc: Andrii Nakryiko <andrii@kernel.org>
Cc: Eduard Zingerman <eddyz87@gmail.com>
Cc: Kumar Kartikeya Dwivedi <memxor@gmail.com>
Cc: Zi Yan <ziy@nvidia.com>
Cc: Baolin Wang <baolin.wang@linux.alibaba.com>
Cc: Nico Pache <nico.pache@linux.dev>
Cc: Ryan Roberts <ryan.roberts@arm.com>
Cc: Dev Jain <dev.jain@arm.com>
Cc: Barry Song <baohua@kernel.org>
Cc: Lance Yang <lance.yang@linux.dev>
Cc: Usama Arif <usama.arif@linux.dev>
Cc: Kiryl Shutsemau <kas@kernel.org>
Cc: Doug Gilbert <dgilbert@interlog.com>
Cc: James Bottomley <james.bottomley@hansenpartnership.com>
Cc: Martin K. Petersen <mkp@kernel.org>
Cc: Simona Vetter <simona@ffwll.ch>
Cc: Helge Deller <deller@gmx.de>
Cc: Sebastian Reichel <sre@kernel.org>
Cc: John Hubbard <jhubbard@nvidia.com>
Cc: Peter Xu <peterx@redhat.com>
Cc: Masami Hiramatsu <mhiramat@kernel.org>
Cc: Oleg Nesterov <oleg@redhat.com>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Thomas Gleixner <tglx@kernel.org>
Cc: Ingo Molnar <mingo@redhat.com>
Cc: Borislav Petkov <bp@alien8.de>
Cc: Dave Hansen <dave.hansen@linux.intel.com>
Cc: Arnaldo Carvalho de Melo <acme@kernel.org>
Cc: Namhyung Kim <namhyung@kernel.org>
Cc: Mark Rutland <mark.rutland@arm.com>
Cc: Rik van Riel <riel@surriel.com>
Cc: Harry Yoo <harry@kernel.org>
Cc: Juri Lelli <juri.lelli@redhat.com>
Cc: Vincent Guittot <vincent.guittot@linaro.org>
Cc: Maarten Lankhorst <maarten.lankhorst@linux.intel.com>
Cc: Maxime Ripard <mripard@kernel.org>
Cc: Thomas Zimmermann <tzimmermann@suse.de>
Cc: David Airlie <airlied@gmail.com>
Cc: Will Deacon <will@kernel.org>
Cc: Aneesh Kumar K.V <aneesh.kumar@kernel.org>
Cc: Nicholas Piggin <npiggin@gmail.com>
Cc: Arnd Bergmann <arnd@arndb.de>
Cc: Muchun Song <muchun.song@linux.dev>
Cc: Oscar Salvador <osalvador@suse.de>
Cc: Matthew Wilcox (Oracle) <willy@infradead.org>
Cc: Jan Kara <jack@suse.cz>
Cc: Marc Zyngier <maz@kernel.org>
Cc: Oliver Upton <oupton@kernel.org>
Cc: Catalin Marinas <catalin.marinas@arm.com>
Cc: Madhavan Srinivasan <maddy@linux.ibm.com>
Cc: Anup Patel <anup@brainfault.org>
Cc: Paul Walmsley <pjw@kernel.org>
Cc: Palmer Dabbelt <palmer@dabbelt.com>
Cc: Albert Ou <aou@eecs.berkeley.edu>
Cc: Christian Borntraeger <borntraeger@linux.ibm.com>
Cc: Janosch Frank <frankja@linux.ibm.com>
Cc: Claudio Imbrenda <imbrenda@linux.ibm.com>
Cc: Alexander Gordeev <agordeev@linux.ibm.com>
Cc: Gerald Schaefer <gerald.schaefer@linux.ibm.com>
Cc: Heiko Carstens <hca@linux.ibm.com>
Cc: Vasily Gorbik <gor@linux.ibm.com>
Cc: David S. Miller <davem@davemloft.net>
Cc: Andreas Larsson <andreas@gaisler.com>
Cc: Al Viro <viro@zeniv.linux.org.uk>
Cc: Christian Brauner <brauner@kernel.org>
Cc: Matthew Brost <matthew.brost@intel.com>
Cc: Joshua Hahn <joshua.hahnjy@gmail.com>
Cc: Rakie Kim <rakie.kim@sk.com>
Cc: Byungchul Park <byungchul@sk.com>
Cc: Gregory Price <gourry@gourry.net>
Cc: Ying Huang <ying.huang@linux.alibaba.com>
Cc: Alistair Popple <apopple@nvidia.com>
Cc: Chris Li <chrisl@kernel.org>
Cc: Kairui Song <kasong@tencent.com>
Cc: Kemeng Shi <shikemeng@huaweicloud.com>
Cc: Nhat Pham <nphamcs@gmail.com>
Cc: Baoquan He <baoquan.he@linux.dev>
Cc: Youngjun Park <youngjun.park@lge.com>
Cc: Johannes Weiner <hannes@cmpxchg.org>
Cc: Qi Zheng <qi.zheng@linux.dev>
Cc: Shakeel Butt <shakeel.butt@linux.dev>
Cc: Axel Rasmussen <axelrasmussen@google.com>
Cc: Yuanchu Xie <yuanchu@google.com>
Cc: Wei Xu <weixugc@google.com>
Cc: Chengming Zhou <chengming.zhou@linux.dev>
Cc: Michal Hocko <mhocko@kernel.org>
Cc: Miklos Szeredi <miklos@szeredi.hu>
Cc: Xu Xin <xu.xin@linux.dev>
Cc: Ihor Solodrai <ihor.solodrai@linux.dev>
Cc: Bert Karwatzki <spasswolf@web.de>
---
mm/internal.h | 1
mm/util.c | 5 ++
mm/vma.c | 81 +++++++++++++++++++++++++++++-------------------
mm/vma.h | 6 ++-
4 files changed, 58 insertions(+), 35 deletions(-)
--- a/mm/internal.h~mm-vma-fix-mmap_prepare-file-handling-remove-file_doesnt_need_get
+++ a/mm/internal.h
@@ -7,6 +7,7 @@
#ifndef __MM_INTERNAL_H
#define __MM_INTERNAL_H
+#include <linux/file.h>
#include <linux/fs.h>
#include <linux/khugepaged.h>
#include <linux/mm.h>
--- a/mm/util.c~mm-vma-fix-mmap_prepare-file-handling-remove-file_doesnt_need_get
+++ a/mm/util.c
@@ -1228,8 +1228,11 @@ int __compat_vma_mmap(struct vm_area_des
/* Perform any preparatory tasks for mmap action. */
err = mmap_action_prepare(desc);
- if (err)
+ if (err) {
+ if (desc->vm_file != vma->vm_file)
+ fput(desc->vm_file);
return err;
+ }
/* Update the VMA from the descriptor. */
compat_set_vma_from_desc(vma, desc);
/* Complete any specified mmap actions. */
--- a/mm/vma.c~mm-vma-fix-mmap_prepare-file-handling-remove-file_doesnt_need_get
+++ a/mm/vma.c
@@ -24,7 +24,8 @@ struct mmap_state {
vm_flags_t vm_flags;
vma_flags_t vma_flags;
};
- struct file *file;
+ struct file *file; /* mmap()-specified file. */
+ struct file *vm_file; /* May be updated by mmap_prepare. */
pgprot_t page_prot;
/* User-defined fields, perhaps updated by .mmap_prepare(). */
@@ -43,8 +44,6 @@ struct mmap_state {
/* Determine if we can check KSM flags early in mmap() logic. */
bool check_ksm_early :1;
- /* If .mmap_prepare changed the file, we don't need to pin. */
- bool file_doesnt_need_get :1;
};
#define MMAP_STATE(name, mm_, vmi_, addr_, len_, pgoff_, anon_pgoff_, vma_flags_, file_) \
@@ -58,6 +57,7 @@ struct mmap_state {
.pglen = PHYS_PFN(len_), \
.vma_flags = vma_flags_, \
.file = file_, \
+ .vm_file = file_, \
.page_prot = vma_flags_to_page_prot(vma_flags_), \
}
@@ -70,7 +70,7 @@ struct mmap_state {
.vma_flags = (map_)->vma_flags, \
.pgoff = (map_)->pgoff, \
.anon_pgoff = (map_)->anon_pgoff, \
- .file = (map_)->file, \
+ .file = (map_)->vm_file, \
.prev = (map_)->prev, \
.middle = vma_, \
.next = (vma_) ? NULL : (map_)->next, \
@@ -2462,7 +2462,7 @@ void mm_drop_all_locks(struct mm_struct
*/
static bool accountable_mapping(struct mmap_state *map)
{
- const struct file *file = map->file;
+ const struct file *file = map->vm_file;
/*
* hugetlb has its own accounting separate from the core VM
@@ -2511,7 +2511,7 @@ static void vms_abort_munmap_vmas(struct
static void update_ksm_flags(struct mmap_state *map)
{
- map->vma_flags = ksm_vma_flags(map->mm, map->file, map->vma_flags);
+ map->vma_flags = ksm_vma_flags(map->mm, map->vm_file, map->vma_flags);
}
static void set_desc_from_map(struct vm_area_desc *desc,
@@ -2521,7 +2521,7 @@ static void set_desc_from_map(struct vm_
desc->end = map->end;
desc->pgoff = map->pgoff;
- desc->vm_file = map->file;
+ desc->vm_file = map->vm_file;
desc->vma_flags = map->vma_flags;
desc->page_prot = map->page_prot;
}
@@ -2601,6 +2601,10 @@ static int __mmap_setup(struct mmap_stat
return 0;
}
+static bool map_same_file(struct mmap_state *map)
+{
+ return map->vm_file == map->file;
+}
static int __mmap_new_file_vma(struct mmap_state *map,
struct vm_area_struct *vma)
@@ -2608,20 +2612,23 @@ static int __mmap_new_file_vma(struct mm
struct vma_iterator *vmi = map->vmi;
int error;
- vma->vm_file = map->file;
- if (!map->file_doesnt_need_get)
- get_file(map->file);
+ vma->vm_file = map->vm_file;
+ if (map_same_file(map))
+ get_file(map->vm_file);
- if (!map->file->f_op->mmap)
+ if (!map->vm_file->f_op->mmap)
return 0;
error = mmap_file(vma->vm_file, vma);
+ map->vm_file = vma->vm_file;
+
if (error) {
UNMAP_STATE(unmap, vmi, vma, vma->vm_start, vma->vm_end,
map->prev, map->next);
- fput(vma->vm_file);
- vma->vm_file = NULL;
+ if (map_same_file(map))
+ fput(map->vm_file);
+ vma->vm_file = NULL;
vma_iter_set(vmi, vma->vm_end);
/* Undo any partial mapping done by a device driver. */
unmap_region(&unmap);
@@ -2638,7 +2645,6 @@ static int __mmap_new_file_vma(struct mm
!vma_flags_test(&map->vma_flags, VMA_MAYWRITE_BIT) &&
vma_test(vma, VMA_MAYWRITE_BIT));
- map->file = vma->vm_file;
map->vma_flags = vma->flags;
return 0;
@@ -2646,7 +2652,7 @@ static int __mmap_new_file_vma(struct mm
static void map_set_anon(struct mmap_state *map)
{
- map->file = NULL;
+ map->vm_file = NULL;
map->vm_ops = NULL;
map->pgoff = map->addr >> PAGE_SHIFT;
}
@@ -2658,7 +2664,7 @@ static bool map_is_private(const struct
static bool map_is_anon(const struct mmap_state *map)
{
- return map_is_private(map) && !map->file;
+ return map_is_private(map) && !map->vm_file;
}
/*
@@ -2703,7 +2709,7 @@ static int __mmap_new_vma(struct mmap_st
}
/* Invoke callbacks. */
- if (map->file)
+ if (map->vm_file)
error = __mmap_new_file_vma(map, vma);
else if (!is_anon)
error = shmem_zero_setup(vma);
@@ -2812,10 +2818,14 @@ static int call_mmap_prepare(struct mmap
int err;
/* Invoke the hook. */
- err = vfs_mmap_prepare(map->file, desc);
+ err = vfs_mmap_prepare(map->vm_file, desc);
if (err)
return err;
+ /* Update first so file refcount tracked correctly. */
+ if (desc->vm_file != map->vm_file)
+ map->vm_file = desc->vm_file;
+
/* It's invalid for mmap_prepare hooks to clear vm_ops. */
if (!desc->vm_ops)
return -EINVAL;
@@ -2826,10 +2836,6 @@ static int call_mmap_prepare(struct mmap
/* Update fields permitted to be changed. */
map->pgoff = desc->pgoff;
- if (desc->vm_file != map->file) {
- map->file_doesnt_need_get = true;
- map->file = desc->vm_file;
- }
map->vma_flags = desc->vma_flags;
map->page_prot = desc->page_prot;
/* User-defined fields. */
@@ -2841,7 +2847,7 @@ static int call_mmap_prepare(struct mmap
* anonymous mappings. Rather than allowing these mappings to be odd
* outliers, simply make them truly anonymous.
*/
- if (map_is_private(map) && file_is_dev_zero(map->file))
+ if (map_is_private(map) && file_is_dev_zero(map->vm_file))
map_set_anon(map);
return 0;
@@ -2860,7 +2866,7 @@ static void set_vma_user_defined_fields(
*/
static bool can_set_ksm_flags_early(struct mmap_state *map)
{
- struct file *file = map->file;
+ struct file *file = map->vm_file;
/* Anonymous mappings have no driver which can change them. */
if (!file)
@@ -2883,6 +2889,20 @@ static bool can_set_ksm_flags_early(stru
return false;
}
+static void map_put_vm_file(struct mmap_state *map)
+{
+ /*
+ * An error occurred or the VMA was merged.
+ *
+ * If the file was changed by the driver (which is required to increment
+ * the replacement file's reference count), drop its reference count.
+ *
+ * On error, the caller always drops the original file regardless.
+ */
+ if (map->vm_file && !map_same_file(map))
+ fput(map->vm_file);
+}
+
static unsigned long __mmap_region(struct file *file, unsigned long addr,
unsigned long len, vma_flags_t vma_flags,
unsigned long pgoff, struct list_head *uf)
@@ -2937,7 +2957,10 @@ static unsigned long __mmap_region(struc
__mmap_complete(&map, vma);
- if (have_mmap_prepare && allocated_new) {
+ if (!allocated_new) {
+ /* Merged, so need to drop refcount. */
+ map_put_vm_file(&map);
+ } else if (have_mmap_prepare) {
error = mmap_action_complete(vma, &desc.action,
/*is_compat=*/false);
if (error)
@@ -2951,13 +2974,7 @@ unacct_error:
if (map.charged)
vm_unacct_memory(map.charged);
abort_munmap:
- /*
- * This indicates that .mmap_prepare has set a new file, differing from
- * desc->vm_file. But since we're aborting the operation, only the
- * original file will be cleaned up. Ensure we clean up both.
- */
- if (map.file_doesnt_need_get)
- fput(map.file);
+ map_put_vm_file(&map);
vms_abort_munmap_vmas(&map.vms, &map.mas_detach);
return error;
}
--- a/mm/vma.h~mm-vma-fix-mmap_prepare-file-handling-remove-file_doesnt_need_get
+++ a/mm/vma.h
@@ -394,8 +394,10 @@ static inline void compat_set_vma_from_d
/* Mutable fields. Populated with initial state. */
vma_set_pgoff(vma, desc->pgoff);
- if (desc->vm_file != vma->vm_file)
- vma_set_file(vma, desc->vm_file);
+ if (desc->vm_file != vma->vm_file) {
+ fput(vma->vm_file);
+ vma->vm_file = desc->vm_file;
+ }
vma->flags = desc->vma_flags;
vma->vm_page_prot = desc->page_prot;
_
Patches currently in -mm which might be from ljs@kernel.org are
mm-vma-fix-mmap_prepare-file-handling-remove-file_doesnt_need_get.patch
mm-vma-introduce-and-use-vma_can_merge.patch
mm-consistently-validate-vma-state-after-mmap-hooks.patch
mm-vma-ensure-mmap_prepare-doesnt-set-actions-on-a-mergeable-vma.patch
mm-make-map_kernel_pages_-internal-and-unexported.patch
mm-vma-tidy-up-map-kernel-pages-enum-values.patch
mm-assert-mmap-lock-held-use-new-flags-api-in-vm_insert_page.patch
mm-add-mmap-action-for-discontiguous-kernel-page-mapping.patch
docs-filesystems-update-mmap_prepare-docs-for-discontig-kernel-pgs.patch
drivers-usb-mon-update-to-use-mmap_prepare-map-kernel-pages.patch
infiniband-update-hfi1-to-use-remap_vmalloc_range.patch
selinux-reject-writable-opens-of-policy-file-drop-mmap-shared-write-check.patch
alsa-pcm-use-vm_insert_page-to-map-pcm-status-page.patch
bpf-arena-mark-arena_map_mmap-mappings-vm_mixedmap.patch
mm-vma-add-vma_is_mm_managed-predicates.patch
mm-vma-only-allow-mmap-to-clear-vma_maywrite_bit-if-not-mm-managed.patch
mm-vma-add-and-use-vma__is_fixed_mapping.patch
scsi-sg-convert-mmap-hook-to-mmap_prepare-and-rework.patch
fbdev-defio-assert-fbinfo_virtfb-drop-vm_io-add-vm_mixedmap.patch
hsi-cmt_speech-convert-mmap-hook-to-mmap_prepare-refactor.patch
mm-gup-error-out-early-on-vma_mayread_bit-vmas.patch
uprobes-remove-vm_io-set-vm_mixedmap-for-mapped-kernel-pages.patch
mm-mlock-clear-vma_locked_mask-over-mmap-callback.patch
mm-mlock-eliminate-weird-vma_io_bit-abuse-and-simplify.patch
mm-vma-enforce-that-mm-managed-mappings-may-not-set-vma_io_bit.patch
mm-remove-vma_io_bit-check-in-vma_is_mm_managed.patch
mm-remove-hugetlb_inlineh.patch
mm-rename-is_vm_hugetlb_page-to-vma_is_hugetlb.patch
mm-drop-some-redundant-checks-around-hugetlb-vmas.patch
mm-vma-introduce-vma_is_mm_backed.patch
mm-madvise-update-is_valid_guard_vma-to-use-vma_is_mm_backed.patch
mm-uffd-use-predicates-for-userfaultfd-checks.patch
mm-madvise-use-predicates-for-madvise-madv_dofork.patch
mm-eliminate-vma_special_flags-usage-when-hugetlb-explicitly-tested.patch
mm-eliminate-vma_special_flags-check-in-lru_gen_look_around.patch
mm-avoid-use-of-vma_special_flags-in-migrate_vma_setup.patch
mm-eliminate-vm_special-vma_special_flags.patch
fuse-dax-do-not-set-vm_mixedmap.patch
mm-huge_memory-remove-vma_is_special_huge.patch
mm-vma-const-ify-vma_assert_stabilised-and-associated-functions.patch
mm-implement-and-use-vma_has_anon_rmap-silence-kcsan.patch
mm-update-comments-to-refer-to-anon-rmap-rather-than-anon_vma.patch
mm-vma-dont-remove-vma-from-rmap-if-pgoff-unchanged.patch
^ permalink raw reply [flat|nested] 5+ messages in thread* + mm-vma-fix-mmap_prepare-file-handling-remove-file_doesnt_need_get.patch added to mm-unstable branch
@ 2026-10-03 23:58 Andrew Morton
0 siblings, 0 replies; 5+ messages in thread
From: Andrew Morton @ 2026-10-03 23:58 UTC (permalink / raw)
To: mm-commits, ljs, akpm
The patch titled
Subject: mm/vma: fix mmap_prepare file handling, remove file_doesnt_need_get
has been added to the -mm mm-unstable branch. Its filename is
mm-vma-fix-mmap_prepare-file-handling-remove-file_doesnt_need_get.patch
This patch will shortly appear at
https://git.kernel.org/pub/scm/linux/kernel/git/akpm/25-new.git/tree/patches/mm-vma-fix-mmap_prepare-file-handling-remove-file_doesnt_need_get.patch
This patch will later appear in the mm-unstable branch at
git://git.kernel.org/pub/scm/linux/kernel/git/akpm/mm
Before you just go and hit "reply", please:
a) Consider who else should be cc'ed
b) Prefer to cc a suitable mailing list as well
c) Ideally: find the original patch on the mailing list and do a
reply-to-all to that, adding suitable additional cc's
*** Remember to use Documentation/process/submit-checklist.rst when testing your code ***
The -mm tree is included into linux-next via various
branches at git://git.kernel.org/pub/scm/linux/kernel/git/akpm/mm
and is updated there most days
------------------------------------------------------
From: "Lorenzo Stoakes (ARM)" <ljs@kernel.org>
Subject: mm/vma: fix mmap_prepare file handling, remove file_doesnt_need_get
Date: Sat, 03 Oct 2026 17:32:32 +0100
The map->file_doesnt_need_get flag is confusing and the existing
implementation has holes.
Drivers are permitted to change the owning file of a mapping. If they do
so, they are required to take a reference on that file.
The mmap() operation which ultimately invokes __mmap_region() is guaranteed
to drop the refcount for the original file the mapping was made under, but
this is not true for the replaced file.
This has been addressed so far by tracking map->file_doesnt_need_get, which
is rather poorly named and unfortunately fails to correctly track whether
or not an additional put were needed in a number of cases.
Make life easier by removing this flag, and instead drop the reference for
both mmap_prepare and the deprecated mmap callback in a new function
map_put_vm_file().
Track whether this needs to be done by aligning mmap_state with
vm_area_desc and store the original file in the map->file field, keeping
the updated file in map->vm_file.
In order to have the same behaviour for both types of hooks, only drop the
reference __mmap_new_file_vma() itself took in its error path, deferring
the replaced file's reference to map_put_vm_file().
To make this work correctly, map->vm_file has to be updated before any
error handling, so update __mmap_new_file_vma() and call_mmap_prepare() to
set this field first.
Also when mmap_prepare() changes the file and is then merged, the reference
count also must be decremented, so update the logic to call
map_put_vm_file() in this case too.
Also update __compat_vma_mmap() to manually perform this step for stacked
file systems using the compatibility layer, and update
compat_set_vma_from_desc() to replace vma_set_file() with a correct
refcount/file update.
No in-tree driver is impacted by the incorrect implementation of this
currently (no driver that does this is mergeable for one), so this does not
need to be a fix.
Link: https://lore.kernel.org/20261003-b4-mmap-prepare-vma-flag-sanify-v4-1-a1f052500fd7@kernel.org
Signed-off-by: Lorenzo Stoakes (ARM) <ljs@kernel.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Cc: Liam R. Howlett <liam@infradead.org>
Cc: Vlastimil Babka <vbabka@kernel.org>
Cc: Jann Horn <jannh@google.com>
Cc: Pedro Falcato <pfalcato@suse.de>
Cc: David Hildenbrand <david@kernel.org>
Cc: Mike Rapoport <rppt@kernel.org>
Cc: Suren Baghdasaryan <surenb@google.com>
Cc: Michal Hocko <mhocko@suse.com>
Cc: Jonathan Corbet <corbet@lwn.net>
Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Cc: Dennis Dalessandro <dennis.dalessandro@cornelisnetworks.com>
Cc: Jason Gunthorpe <jgg@ziepe.ca>
Cc: Leon Romanovsky <leon@kernel.org>
Cc: Paul Moore <paul@paul-moore.com>
Cc: Stephen Smalley <stephen.smalley.work@gmail.com>
Cc: Jaroslav Kysela <perex@perex.cz>
Cc: Takashi Iwai <tiwai@suse.com>
Cc: Alexei Starovoitov <ast@kernel.org>
Cc: Daniel Borkmann <daniel@iogearbox.net>
Cc: Andrii Nakryiko <andrii@kernel.org>
Cc: Eduard Zingerman <eddyz87@gmail.com>
Cc: Kumar Kartikeya Dwivedi <memxor@gmail.com>
Cc: Zi Yan <ziy@nvidia.com>
Cc: Baolin Wang <baolin.wang@linux.alibaba.com>
Cc: Nico Pache <nico.pache@linux.dev>
Cc: Ryan Roberts <ryan.roberts@arm.com>
Cc: Dev Jain <dev.jain@arm.com>
Cc: Barry Song <baohua@kernel.org>
Cc: Lance Yang <lance.yang@linux.dev>
Cc: Usama Arif <usama.arif@linux.dev>
Cc: Kiryl Shutsemau <kas@kernel.org>
Cc: Doug Gilbert <dgilbert@interlog.com>
Cc: James Bottomley <james.bottomley@hansenpartnership.com>
Cc: Martin K. Petersen <mkp@kernel.org>
Cc: Simona Vetter <simona@ffwll.ch>
Cc: Helge Deller <deller@gmx.de>
Cc: Sebastian Reichel <sre@kernel.org>
Cc: John Hubbard <jhubbard@nvidia.com>
Cc: Peter Xu <peterx@redhat.com>
Cc: Masami Hiramatsu <mhiramat@kernel.org>
Cc: Oleg Nesterov <oleg@redhat.com>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Thomas Gleixner <tglx@kernel.org>
Cc: Ingo Molnar <mingo@redhat.com>
Cc: Borislav Petkov <bp@alien8.de>
Cc: Dave Hansen <dave.hansen@linux.intel.com>
Cc: Arnaldo Carvalho de Melo <acme@kernel.org>
Cc: Namhyung Kim <namhyung@kernel.org>
Cc: Mark Rutland <mark.rutland@arm.com>
Cc: Rik van Riel <riel@surriel.com>
Cc: Harry Yoo <harry@kernel.org>
Cc: Juri Lelli <juri.lelli@redhat.com>
Cc: Vincent Guittot <vincent.guittot@linaro.org>
Cc: Maarten Lankhorst <maarten.lankhorst@linux.intel.com>
Cc: Maxime Ripard <mripard@kernel.org>
Cc: Thomas Zimmermann <tzimmermann@suse.de>
Cc: David Airlie <airlied@gmail.com>
Cc: Will Deacon <will@kernel.org>
Cc: Aneesh Kumar K.V <aneesh.kumar@kernel.org>
Cc: Nicholas Piggin <npiggin@gmail.com>
Cc: Arnd Bergmann <arnd@arndb.de>
Cc: Muchun Song <muchun.song@linux.dev>
Cc: Oscar Salvador <osalvador@suse.de>
Cc: Matthew Wilcox (Oracle) <willy@infradead.org>
Cc: Jan Kara <jack@suse.cz>
Cc: Marc Zyngier <maz@kernel.org>
Cc: Oliver Upton <oupton@kernel.org>
Cc: Catalin Marinas <catalin.marinas@arm.com>
Cc: Madhavan Srinivasan <maddy@linux.ibm.com>
Cc: Anup Patel <anup@brainfault.org>
Cc: Paul Walmsley <pjw@kernel.org>
Cc: Palmer Dabbelt <palmer@dabbelt.com>
Cc: Albert Ou <aou@eecs.berkeley.edu>
Cc: Christian Borntraeger <borntraeger@linux.ibm.com>
Cc: Janosch Frank <frankja@linux.ibm.com>
Cc: Claudio Imbrenda <imbrenda@linux.ibm.com>
Cc: Alexander Gordeev <agordeev@linux.ibm.com>
Cc: Gerald Schaefer <gerald.schaefer@linux.ibm.com>
Cc: Heiko Carstens <hca@linux.ibm.com>
Cc: Vasily Gorbik <gor@linux.ibm.com>
Cc: David S. Miller <davem@davemloft.net>
Cc: Andreas Larsson <andreas@gaisler.com>
Cc: Al Viro <viro@zeniv.linux.org.uk>
Cc: Christian Brauner <brauner@kernel.org>
Cc: Matthew Brost <matthew.brost@intel.com>
Cc: Joshua Hahn <joshua.hahnjy@gmail.com>
Cc: Rakie Kim <rakie.kim@sk.com>
Cc: Byungchul Park <byungchul@sk.com>
Cc: Gregory Price <gourry@gourry.net>
Cc: Ying Huang <ying.huang@linux.alibaba.com>
Cc: Alistair Popple <apopple@nvidia.com>
Cc: Chris Li <chrisl@kernel.org>
Cc: Kairui Song <kasong@tencent.com>
Cc: Kemeng Shi <shikemeng@huaweicloud.com>
Cc: Nhat Pham <nphamcs@gmail.com>
Cc: Baoquan He <baoquan.he@linux.dev>
Cc: Youngjun Park <youngjun.park@lge.com>
Cc: Johannes Weiner <hannes@cmpxchg.org>
Cc: Qi Zheng <qi.zheng@linux.dev>
Cc: Shakeel Butt <shakeel.butt@linux.dev>
Cc: Axel Rasmussen <axelrasmussen@google.com>
Cc: Yuanchu Xie <yuanchu@google.com>
Cc: Wei Xu <weixugc@google.com>
Cc: Chengming Zhou <chengming.zhou@linux.dev>
Cc: Michal Hocko <mhocko@kernel.org>
Cc: Miklos Szeredi <miklos@szeredi.hu>
Cc: Xu Xin <xu.xin@linux.dev>
---
mm/internal.h | 1
mm/util.c | 5 ++
mm/vma.c | 81 +++++++++++++++++++++++++++++-------------------
mm/vma.h | 6 ++-
4 files changed, 58 insertions(+), 35 deletions(-)
--- a/mm/internal.h~mm-vma-fix-mmap_prepare-file-handling-remove-file_doesnt_need_get
+++ a/mm/internal.h
@@ -7,6 +7,7 @@
#ifndef __MM_INTERNAL_H
#define __MM_INTERNAL_H
+#include <linux/file.h>
#include <linux/fs.h>
#include <linux/khugepaged.h>
#include <linux/mm.h>
--- a/mm/util.c~mm-vma-fix-mmap_prepare-file-handling-remove-file_doesnt_need_get
+++ a/mm/util.c
@@ -1228,8 +1228,11 @@ int __compat_vma_mmap(struct vm_area_des
/* Perform any preparatory tasks for mmap action. */
err = mmap_action_prepare(desc);
- if (err)
+ if (err) {
+ if (desc->vm_file != vma->vm_file)
+ fput(desc->vm_file);
return err;
+ }
/* Update the VMA from the descriptor. */
compat_set_vma_from_desc(vma, desc);
/* Complete any specified mmap actions. */
--- a/mm/vma.c~mm-vma-fix-mmap_prepare-file-handling-remove-file_doesnt_need_get
+++ a/mm/vma.c
@@ -24,7 +24,8 @@ struct mmap_state {
vm_flags_t vm_flags;
vma_flags_t vma_flags;
};
- struct file *file;
+ struct file *file; /* mmap()-specified file. */
+ struct file *vm_file; /* May be updated by mmap_prepare. */
pgprot_t page_prot;
/* User-defined fields, perhaps updated by .mmap_prepare(). */
@@ -43,8 +44,6 @@ struct mmap_state {
/* Determine if we can check KSM flags early in mmap() logic. */
bool check_ksm_early :1;
- /* If .mmap_prepare changed the file, we don't need to pin. */
- bool file_doesnt_need_get :1;
};
#define MMAP_STATE(name, mm_, vmi_, addr_, len_, pgoff_, anon_pgoff_, vma_flags_, file_) \
@@ -58,6 +57,7 @@ struct mmap_state {
.pglen = PHYS_PFN(len_), \
.vma_flags = vma_flags_, \
.file = file_, \
+ .vm_file = file_, \
.page_prot = vma_flags_to_page_prot(vma_flags_), \
}
@@ -70,7 +70,7 @@ struct mmap_state {
.vma_flags = (map_)->vma_flags, \
.pgoff = (map_)->pgoff, \
.anon_pgoff = (map_)->anon_pgoff, \
- .file = (map_)->file, \
+ .file = (map_)->vm_file, \
.prev = (map_)->prev, \
.middle = vma_, \
.next = (vma_) ? NULL : (map_)->next, \
@@ -2462,7 +2462,7 @@ void mm_drop_all_locks(struct mm_struct
*/
static bool accountable_mapping(struct mmap_state *map)
{
- const struct file *file = map->file;
+ const struct file *file = map->vm_file;
/*
* hugetlb has its own accounting separate from the core VM
@@ -2511,7 +2511,7 @@ static void vms_abort_munmap_vmas(struct
static void update_ksm_flags(struct mmap_state *map)
{
- map->vma_flags = ksm_vma_flags(map->mm, map->file, map->vma_flags);
+ map->vma_flags = ksm_vma_flags(map->mm, map->vm_file, map->vma_flags);
}
static void set_desc_from_map(struct vm_area_desc *desc,
@@ -2521,7 +2521,7 @@ static void set_desc_from_map(struct vm_
desc->end = map->end;
desc->pgoff = map->pgoff;
- desc->vm_file = map->file;
+ desc->vm_file = map->vm_file;
desc->vma_flags = map->vma_flags;
desc->page_prot = map->page_prot;
}
@@ -2601,6 +2601,10 @@ static int __mmap_setup(struct mmap_stat
return 0;
}
+static bool map_same_file(struct mmap_state *map)
+{
+ return map->vm_file == map->file;
+}
static int __mmap_new_file_vma(struct mmap_state *map,
struct vm_area_struct *vma)
@@ -2608,20 +2612,23 @@ static int __mmap_new_file_vma(struct mm
struct vma_iterator *vmi = map->vmi;
int error;
- vma->vm_file = map->file;
- if (!map->file_doesnt_need_get)
- get_file(map->file);
+ vma->vm_file = map->vm_file;
+ if (map_same_file(map))
+ get_file(map->vm_file);
- if (!map->file->f_op->mmap)
+ if (!map->vm_file->f_op->mmap)
return 0;
error = mmap_file(vma->vm_file, vma);
+ map->vm_file = vma->vm_file;
+
if (error) {
UNMAP_STATE(unmap, vmi, vma, vma->vm_start, vma->vm_end,
map->prev, map->next);
- fput(vma->vm_file);
- vma->vm_file = NULL;
+ if (map_same_file(map))
+ fput(map->vm_file);
+ vma->vm_file = NULL;
vma_iter_set(vmi, vma->vm_end);
/* Undo any partial mapping done by a device driver. */
unmap_region(&unmap);
@@ -2638,7 +2645,6 @@ static int __mmap_new_file_vma(struct mm
!vma_flags_test(&map->vma_flags, VMA_MAYWRITE_BIT) &&
vma_test(vma, VMA_MAYWRITE_BIT));
- map->file = vma->vm_file;
map->vma_flags = vma->flags;
return 0;
@@ -2646,7 +2652,7 @@ static int __mmap_new_file_vma(struct mm
static void map_set_anon(struct mmap_state *map)
{
- map->file = NULL;
+ map->vm_file = NULL;
map->vm_ops = NULL;
map->pgoff = map->addr >> PAGE_SHIFT;
}
@@ -2658,7 +2664,7 @@ static bool map_is_private(const struct
static bool map_is_anon(const struct mmap_state *map)
{
- return map_is_private(map) && !map->file;
+ return map_is_private(map) && !map->vm_file;
}
/*
@@ -2703,7 +2709,7 @@ static int __mmap_new_vma(struct mmap_st
}
/* Invoke callbacks. */
- if (map->file)
+ if (map->vm_file)
error = __mmap_new_file_vma(map, vma);
else if (!is_anon)
error = shmem_zero_setup(vma);
@@ -2812,10 +2818,14 @@ static int call_mmap_prepare(struct mmap
int err;
/* Invoke the hook. */
- err = vfs_mmap_prepare(map->file, desc);
+ err = vfs_mmap_prepare(map->vm_file, desc);
if (err)
return err;
+ /* Update first so file refcount tracked correctly. */
+ if (desc->vm_file != map->vm_file)
+ map->vm_file = desc->vm_file;
+
/* It's invalid for mmap_prepare hooks to clear vm_ops. */
if (!desc->vm_ops)
return -EINVAL;
@@ -2826,10 +2836,6 @@ static int call_mmap_prepare(struct mmap
/* Update fields permitted to be changed. */
map->pgoff = desc->pgoff;
- if (desc->vm_file != map->file) {
- map->file_doesnt_need_get = true;
- map->file = desc->vm_file;
- }
map->vma_flags = desc->vma_flags;
map->page_prot = desc->page_prot;
/* User-defined fields. */
@@ -2841,7 +2847,7 @@ static int call_mmap_prepare(struct mmap
* anonymous mappings. Rather than allowing these mappings to be odd
* outliers, simply make them truly anonymous.
*/
- if (map_is_private(map) && file_is_dev_zero(map->file))
+ if (map_is_private(map) && file_is_dev_zero(map->vm_file))
map_set_anon(map);
return 0;
@@ -2860,7 +2866,7 @@ static void set_vma_user_defined_fields(
*/
static bool can_set_ksm_flags_early(struct mmap_state *map)
{
- struct file *file = map->file;
+ struct file *file = map->vm_file;
/* Anonymous mappings have no driver which can change them. */
if (!file)
@@ -2883,6 +2889,20 @@ static bool can_set_ksm_flags_early(stru
return false;
}
+static void map_put_vm_file(struct mmap_state *map)
+{
+ /*
+ * An error occurred or the VMA was merged.
+ *
+ * If the file was changed by the driver (which is required to increment
+ * the replacement file's reference count), drop its reference count.
+ *
+ * On error, the caller always drops the original file regardless.
+ */
+ if (map->vm_file && !map_same_file(map))
+ fput(map->vm_file);
+}
+
static unsigned long __mmap_region(struct file *file, unsigned long addr,
unsigned long len, vma_flags_t vma_flags,
unsigned long pgoff, struct list_head *uf)
@@ -2937,7 +2957,10 @@ static unsigned long __mmap_region(struc
__mmap_complete(&map, vma);
- if (have_mmap_prepare && allocated_new) {
+ if (!allocated_new) {
+ /* Merged, so need to drop refcount. */
+ map_put_vm_file(&map);
+ } else if (have_mmap_prepare) {
error = mmap_action_complete(vma, &desc.action,
/*is_compat=*/false);
if (error)
@@ -2951,13 +2974,7 @@ unacct_error:
if (map.charged)
vm_unacct_memory(map.charged);
abort_munmap:
- /*
- * This indicates that .mmap_prepare has set a new file, differing from
- * desc->vm_file. But since we're aborting the operation, only the
- * original file will be cleaned up. Ensure we clean up both.
- */
- if (map.file_doesnt_need_get)
- fput(map.file);
+ map_put_vm_file(&map);
vms_abort_munmap_vmas(&map.vms, &map.mas_detach);
return error;
}
--- a/mm/vma.h~mm-vma-fix-mmap_prepare-file-handling-remove-file_doesnt_need_get
+++ a/mm/vma.h
@@ -394,8 +394,10 @@ static inline void compat_set_vma_from_d
/* Mutable fields. Populated with initial state. */
vma_set_pgoff(vma, desc->pgoff);
- if (desc->vm_file != vma->vm_file)
- vma_set_file(vma, desc->vm_file);
+ if (desc->vm_file != vma->vm_file) {
+ fput(vma->vm_file);
+ vma->vm_file = desc->vm_file;
+ }
vma->flags = desc->vma_flags;
vma->vm_page_prot = desc->page_prot;
_
Patches currently in -mm which might be from ljs@kernel.org are
mm-mremap-fix-locked_vm-leak-from-mremap_dontunmap-self-merge.patch
mm-mremap-fix-locked_vm-leak-by-splitting-vma-for-mremap_dontunmap.patch
drivers-char-mem-mmap-readonly-map_shared-dev-zero-correctly.patch
mm-vmpressure-remove-window-size-todo.patch
tools-testing-selftests-mm-add-missing-gitignore-entries.patch
mm-move-drivers-char-memc-to-mm-char-memc.patch
mm-implement-file_is_dev_zero-to-uniquely-identify-dev-zero.patch
mm-vma-only-permit-map_private-dev-zero-to-be-mapped-anonymous.patch
mm-vma-make-map_private-mapped-dev-zero-mappings-truly-anonymous.patch
tools-testing-vma-add-test-to-assert-map_private-dev-zero-is-anon.patch
tools-testing-selftests-mm-add-map_private-dev-zero-merge-tests.patch
mm-madvise-swap-in-cowd-map_private-file-mappings-on-madv_willneed.patch
mm-khugepaged-deposit-a-newly-allocated-page-table-on-collapse.patch
mm-enable-mmu_gather_rcu_table_free-for-most-2-level-architectures.patch
mm-enable-mmu_gather_rcu_table_free-for-mmu-riscv.patch
mm-enable-mmu_gather_rcu_table_free-for-mmu-arm.patch
mm-enable-mmu_gather_rcu_table_free-for-arc-microblaze-xtensa.patch
mm-enable-mmu_gather_rcu_table_free-for-sparc64.patch
mm-enable-mmu_gather_rcu_table_free-for-m68k-coldfire.patch
mm-enable-mmu_gather_rcu_table_free-for-sh-x2.patch
mm-enable-mmu_gather_rcu_table_free-for-m68k-motorola.patch
mm-enable-mmu_gather_rcu_table_free-for-sparc32.patch
mm-userland-pgtable-freeing-is-rcu-safe-now-remove-leftover-bits.patch
mm-change-the-contract-for-free_pgtables-update-docs.patch
mm-vma-fix-mmap_prepare-file-handling-remove-file_doesnt_need_get.patch
mm-vma-introduce-and-use-vma_can_merge.patch
mm-consistently-validate-vma-state-after-mmap-hooks.patch
mm-vma-ensure-mmap_prepare-doesnt-set-actions-on-a-mergeable-vma.patch
mm-make-map_kernel_pages_-internal-and-unexported.patch
mm-vma-tidy-up-map-kernel-pages-enum-values.patch
mm-add-mmap-action-for-discontiguous-kernel-page-mapping.patch
docs-filesystems-update-mmap_prepare-docs-for-discontig-kernel-pgs.patch
drivers-usb-mon-update-to-use-mmap_prepare-map-kernel-pages.patch
infiniband-update-hfi1-to-use-remap_vmalloc_range.patch
selinux-reject-writable-opens-of-policy-file-drop-mmap-shared-write-check.patch
alsa-pcm-use-vm_insert_page-to-map-pcm-status-page.patch
bpf-arena-mark-arena_map_mmap-mappings-vm_mixedmap.patch
mm-vma-add-vma_is_mm_managed-predicates.patch
mm-vma-only-allow-mmap-to-clear-vma_maywrite_bit-if-not-mm-managed.patch
mm-vma-add-and-use-vma__is_fixed_mapping.patch
scsi-sg-convert-mmap-hook-to-mmap_prepare-and-rework.patch
fbdev-defio-assert-fbinfo_virtfb-drop-vm_io-add-vm_mixedmap.patch
hsi-cmt_speech-convert-mmap-hook-to-mmap_prepare-refactor.patch
mm-gup-error-out-early-on-vma_mayread_bit-vmas.patch
uprobes-remove-vm_io-set-vm_mixedmap-for-mapped-kernel-pages.patch
mm-mlock-clear-vma_locked_mask-over-mmap-callback.patch
mm-mlock-eliminate-weird-vma_io_bit-abuse-and-simplify.patch
mm-vma-enforce-that-mm-managed-mappings-may-not-set-vma_io_bit.patch
mm-remove-vma_io_bit-check-in-vma_is_mm_managed.patch
mm-remove-hugetlb_inlineh.patch
mm-rename-is_vm_hugetlb_page-to-vma_is_hugetlb.patch
mm-drop-some-redundant-checks-around-hugetlb-vmas.patch
mm-madvise-update-is_valid_guard_vma-to-use-vma_can_merge.patch
mm-vma-introduce-vma_is_mm_backed.patch
mm-uffd-use-predicates-for-userfaultfd-checks.patch
mm-madvise-use-predicates-for-madvise-madv_dofork.patch
mm-eliminate-vma_special_flags-usage-when-hugetlb-explicitly-tested.patch
mm-eliminate-vma_special_flags-check-in-lru_gen_look_around.patch
mm-avoid-use-of-vma_special_flags-in-migrate_vma_setup.patch
mm-eliminate-vm_special-vma_special_flags.patch
fuse-dax-do-not-set-vm_mixedmap.patch
mm-huge_memory-remove-vma_is_special_huge.patch
mm-vma-const-ify-vma_assert_stabilised-and-associated-functions.patch
mm-implement-and-use-vma_has_anon_rmap-silence-kcsan.patch
mm-update-comments-to-refer-to-anon-rmap-rather-than-anon_vma.patch
mm-vma-dont-remove-vma-from-rmap-if-pgoff-unchanged.patch
^ permalink raw reply [flat|nested] 5+ messages in thread* + mm-vma-fix-mmap_prepare-file-handling-remove-file_doesnt_need_get.patch added to mm-unstable branch
@ 2026-10-03 23:34 Andrew Morton
0 siblings, 0 replies; 5+ messages in thread
From: Andrew Morton @ 2026-10-03 23:34 UTC (permalink / raw)
To: mm-commits, ljs, akpm
The patch titled
Subject: mm/vma: fix mmap_prepare file handling, remove file_doesnt_need_get
has been added to the -mm mm-unstable branch. Its filename is
mm-vma-fix-mmap_prepare-file-handling-remove-file_doesnt_need_get.patch
This patch will shortly appear at
https://git.kernel.org/pub/scm/linux/kernel/git/akpm/25-new.git/tree/patches/mm-vma-fix-mmap_prepare-file-handling-remove-file_doesnt_need_get.patch
This patch will later appear in the mm-unstable branch at
git://git.kernel.org/pub/scm/linux/kernel/git/akpm/mm
Before you just go and hit "reply", please:
a) Consider who else should be cc'ed
b) Prefer to cc a suitable mailing list as well
c) Ideally: find the original patch on the mailing list and do a
reply-to-all to that, adding suitable additional cc's
*** Remember to use Documentation/process/submit-checklist.rst when testing your code ***
The -mm tree is included into linux-next via various
branches at git://git.kernel.org/pub/scm/linux/kernel/git/akpm/mm
and is updated there most days
------------------------------------------------------
From: "Lorenzo Stoakes (ARM)" <ljs@kernel.org>
Subject: mm/vma: fix mmap_prepare file handling, remove file_doesnt_need_get
Date: Sat, 03 Oct 2026 17:32:32 +0100
The map->file_doesnt_need_get flag is confusing and the existing
implementation has holes.
Drivers are permitted to change the owning file of a mapping. If they do
so, they are required to take a reference on that file.
The mmap() operation which ultimately invokes __mmap_region() is guaranteed
to drop the refcount for the original file the mapping was made under, but
this is not true for the replaced file.
This has been addressed so far by tracking map->file_doesnt_need_get, which
is rather poorly named and unfortunately fails to correctly track whether
or not an additional put were needed in a number of cases.
Make life easier by removing this flag, and instead drop the reference for
both mmap_prepare and the deprecated mmap callback in a new function
map_put_vm_file().
Track whether this needs to be done by aligning mmap_state with
vm_area_desc and store the original file in the map->file field, keeping
the updated file in map->vm_file.
In order to have the same behaviour for both types of hooks, only drop the
reference __mmap_new_file_vma() itself took in its error path, deferring
the replaced file's reference to map_put_vm_file().
To make this work correctly, map->vm_file has to be updated before any
error handling, so update __mmap_new_file_vma() and call_mmap_prepare() to
set this field first.
Also when mmap_prepare() changes the file and is then merged, the reference
count also must be decremented, so update the logic to call
map_put_vm_file() in this case too.
Also update __compat_vma_mmap() to manually perform this step for stacked
file systems using the compatibility layer, and update
compat_set_vma_from_desc() to replace vma_set_file() with a correct
refcount/file update.
No in-tree driver is impacted by the incorrect implementation of this
currently (no driver that does this is mergeable for one), so this does not
need to be a fix.
Link: https://lore.kernel.org/20261003-b4-mmap-prepare-vma-flag-sanify-v4-1-a1f052500fd7@kernel.org
Signed-off-by: Lorenzo Stoakes (ARM) <ljs@kernel.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Cc: Liam R. Howlett <liam@infradead.org>
Cc: Vlastimil Babka <vbabka@kernel.org>
Cc: Jann Horn <jannh@google.com>
Cc: Pedro Falcato <pfalcato@suse.de>
Cc: David Hildenbrand <david@kernel.org>
Cc: Mike Rapoport <rppt@kernel.org>
Cc: Suren Baghdasaryan <surenb@google.com>
Cc: Michal Hocko <mhocko@suse.com>
Cc: Jonathan Corbet <corbet@lwn.net>
Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Cc: Dennis Dalessandro <dennis.dalessandro@cornelisnetworks.com>
Cc: Jason Gunthorpe <jgg@ziepe.ca>
Cc: Leon Romanovsky <leon@kernel.org>
Cc: Paul Moore <paul@paul-moore.com>
Cc: Stephen Smalley <stephen.smalley.work@gmail.com>
Cc: Jaroslav Kysela <perex@perex.cz>
Cc: Takashi Iwai <tiwai@suse.com>
Cc: Alexei Starovoitov <ast@kernel.org>
Cc: Daniel Borkmann <daniel@iogearbox.net>
Cc: Andrii Nakryiko <andrii@kernel.org>
Cc: Eduard Zingerman <eddyz87@gmail.com>
Cc: Kumar Kartikeya Dwivedi <memxor@gmail.com>
Cc: Zi Yan <ziy@nvidia.com>
Cc: Baolin Wang <baolin.wang@linux.alibaba.com>
Cc: Nico Pache <nico.pache@linux.dev>
Cc: Ryan Roberts <ryan.roberts@arm.com>
Cc: Dev Jain <dev.jain@arm.com>
Cc: Barry Song <baohua@kernel.org>
Cc: Lance Yang <lance.yang@linux.dev>
Cc: Usama Arif <usama.arif@linux.dev>
Cc: Kiryl Shutsemau <kas@kernel.org>
Cc: Doug Gilbert <dgilbert@interlog.com>
Cc: James Bottomley <james.bottomley@hansenpartnership.com>
Cc: Martin K. Petersen <mkp@kernel.org>
Cc: Simona Vetter <simona@ffwll.ch>
Cc: Helge Deller <deller@gmx.de>
Cc: Sebastian Reichel <sre@kernel.org>
Cc: John Hubbard <jhubbard@nvidia.com>
Cc: Peter Xu <peterx@redhat.com>
Cc: Masami Hiramatsu <mhiramat@kernel.org>
Cc: Oleg Nesterov <oleg@redhat.com>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Thomas Gleixner <tglx@kernel.org>
Cc: Ingo Molnar <mingo@redhat.com>
Cc: Borislav Petkov <bp@alien8.de>
Cc: Dave Hansen <dave.hansen@linux.intel.com>
Cc: Arnaldo Carvalho de Melo <acme@kernel.org>
Cc: Namhyung Kim <namhyung@kernel.org>
Cc: Mark Rutland <mark.rutland@arm.com>
Cc: Rik van Riel <riel@surriel.com>
Cc: Harry Yoo <harry@kernel.org>
Cc: Juri Lelli <juri.lelli@redhat.com>
Cc: Vincent Guittot <vincent.guittot@linaro.org>
Cc: Maarten Lankhorst <maarten.lankhorst@linux.intel.com>
Cc: Maxime Ripard <mripard@kernel.org>
Cc: Thomas Zimmermann <tzimmermann@suse.de>
Cc: David Airlie <airlied@gmail.com>
Cc: Will Deacon <will@kernel.org>
Cc: Aneesh Kumar K.V <aneesh.kumar@kernel.org>
Cc: Nicholas Piggin <npiggin@gmail.com>
Cc: Arnd Bergmann <arnd@arndb.de>
Cc: Muchun Song <muchun.song@linux.dev>
Cc: Oscar Salvador <osalvador@suse.de>
Cc: Matthew Wilcox (Oracle) <willy@infradead.org>
Cc: Jan Kara <jack@suse.cz>
Cc: Marc Zyngier <maz@kernel.org>
Cc: Oliver Upton <oupton@kernel.org>
Cc: Catalin Marinas <catalin.marinas@arm.com>
Cc: Madhavan Srinivasan <maddy@linux.ibm.com>
Cc: Anup Patel <anup@brainfault.org>
Cc: Paul Walmsley <pjw@kernel.org>
Cc: Palmer Dabbelt <palmer@dabbelt.com>
Cc: Albert Ou <aou@eecs.berkeley.edu>
Cc: Christian Borntraeger <borntraeger@linux.ibm.com>
Cc: Janosch Frank <frankja@linux.ibm.com>
Cc: Claudio Imbrenda <imbrenda@linux.ibm.com>
Cc: Alexander Gordeev <agordeev@linux.ibm.com>
Cc: Gerald Schaefer <gerald.schaefer@linux.ibm.com>
Cc: Heiko Carstens <hca@linux.ibm.com>
Cc: Vasily Gorbik <gor@linux.ibm.com>
Cc: David S. Miller <davem@davemloft.net>
Cc: Andreas Larsson <andreas@gaisler.com>
Cc: Al Viro <viro@zeniv.linux.org.uk>
Cc: Christian Brauner <brauner@kernel.org>
Cc: Matthew Brost <matthew.brost@intel.com>
Cc: Joshua Hahn <joshua.hahnjy@gmail.com>
Cc: Rakie Kim <rakie.kim@sk.com>
Cc: Byungchul Park <byungchul@sk.com>
Cc: Gregory Price <gourry@gourry.net>
Cc: Ying Huang <ying.huang@linux.alibaba.com>
Cc: Alistair Popple <apopple@nvidia.com>
Cc: Chris Li <chrisl@kernel.org>
Cc: Kairui Song <kasong@tencent.com>
Cc: Kemeng Shi <shikemeng@huaweicloud.com>
Cc: Nhat Pham <nphamcs@gmail.com>
Cc: Baoquan He <baoquan.he@linux.dev>
Cc: Youngjun Park <youngjun.park@lge.com>
Cc: Johannes Weiner <hannes@cmpxchg.org>
Cc: Qi Zheng <qi.zheng@linux.dev>
Cc: Shakeel Butt <shakeel.butt@linux.dev>
Cc: Axel Rasmussen <axelrasmussen@google.com>
Cc: Yuanchu Xie <yuanchu@google.com>
Cc: Wei Xu <weixugc@google.com>
Cc: Chengming Zhou <chengming.zhou@linux.dev>
Cc: Michal Hocko <mhocko@kernel.org>
Cc: Miklos Szeredi <miklos@szeredi.hu>
Cc: Xu Xin <xu.xin@linux.dev>
---
mm/internal.h | 1
mm/util.c | 5 ++
mm/vma.c | 81 +++++++++++++++++++++++++++++-------------------
mm/vma.h | 6 ++-
4 files changed, 58 insertions(+), 35 deletions(-)
--- a/mm/internal.h~mm-vma-fix-mmap_prepare-file-handling-remove-file_doesnt_need_get
+++ a/mm/internal.h
@@ -7,6 +7,7 @@
#ifndef __MM_INTERNAL_H
#define __MM_INTERNAL_H
+#include <linux/file.h>
#include <linux/fs.h>
#include <linux/khugepaged.h>
#include <linux/mm.h>
--- a/mm/util.c~mm-vma-fix-mmap_prepare-file-handling-remove-file_doesnt_need_get
+++ a/mm/util.c
@@ -1228,8 +1228,11 @@ int __compat_vma_mmap(struct vm_area_des
/* Perform any preparatory tasks for mmap action. */
err = mmap_action_prepare(desc);
- if (err)
+ if (err) {
+ if (desc->vm_file != vma->vm_file)
+ fput(desc->vm_file);
return err;
+ }
/* Update the VMA from the descriptor. */
compat_set_vma_from_desc(vma, desc);
/* Complete any specified mmap actions. */
--- a/mm/vma.c~mm-vma-fix-mmap_prepare-file-handling-remove-file_doesnt_need_get
+++ a/mm/vma.c
@@ -24,7 +24,8 @@ struct mmap_state {
vm_flags_t vm_flags;
vma_flags_t vma_flags;
};
- struct file *file;
+ struct file *file; /* mmap()-specified file. */
+ struct file *vm_file; /* May be updated by mmap_prepare. */
pgprot_t page_prot;
/* User-defined fields, perhaps updated by .mmap_prepare(). */
@@ -43,8 +44,6 @@ struct mmap_state {
/* Determine if we can check KSM flags early in mmap() logic. */
bool check_ksm_early :1;
- /* If .mmap_prepare changed the file, we don't need to pin. */
- bool file_doesnt_need_get :1;
};
#define MMAP_STATE(name, mm_, vmi_, addr_, len_, pgoff_, anon_pgoff_, vma_flags_, file_) \
@@ -58,6 +57,7 @@ struct mmap_state {
.pglen = PHYS_PFN(len_), \
.vma_flags = vma_flags_, \
.file = file_, \
+ .vm_file = file_, \
.page_prot = vma_flags_to_page_prot(vma_flags_), \
}
@@ -70,7 +70,7 @@ struct mmap_state {
.vma_flags = (map_)->vma_flags, \
.pgoff = (map_)->pgoff, \
.anon_pgoff = (map_)->anon_pgoff, \
- .file = (map_)->file, \
+ .file = (map_)->vm_file, \
.prev = (map_)->prev, \
.middle = vma_, \
.next = (vma_) ? NULL : (map_)->next, \
@@ -2462,7 +2462,7 @@ void mm_drop_all_locks(struct mm_struct
*/
static bool accountable_mapping(struct mmap_state *map)
{
- const struct file *file = map->file;
+ const struct file *file = map->vm_file;
/*
* hugetlb has its own accounting separate from the core VM
@@ -2511,7 +2511,7 @@ static void vms_abort_munmap_vmas(struct
static void update_ksm_flags(struct mmap_state *map)
{
- map->vma_flags = ksm_vma_flags(map->mm, map->file, map->vma_flags);
+ map->vma_flags = ksm_vma_flags(map->mm, map->vm_file, map->vma_flags);
}
static void set_desc_from_map(struct vm_area_desc *desc,
@@ -2521,7 +2521,7 @@ static void set_desc_from_map(struct vm_
desc->end = map->end;
desc->pgoff = map->pgoff;
- desc->vm_file = map->file;
+ desc->vm_file = map->vm_file;
desc->vma_flags = map->vma_flags;
desc->page_prot = map->page_prot;
}
@@ -2601,6 +2601,10 @@ static int __mmap_setup(struct mmap_stat
return 0;
}
+static bool map_same_file(struct mmap_state *map)
+{
+ return map->vm_file == map->file;
+}
static int __mmap_new_file_vma(struct mmap_state *map,
struct vm_area_struct *vma)
@@ -2608,20 +2612,23 @@ static int __mmap_new_file_vma(struct mm
struct vma_iterator *vmi = map->vmi;
int error;
- vma->vm_file = map->file;
- if (!map->file_doesnt_need_get)
- get_file(map->file);
+ vma->vm_file = map->vm_file;
+ if (map_same_file(map))
+ get_file(map->vm_file);
- if (!map->file->f_op->mmap)
+ if (!map->vm_file->f_op->mmap)
return 0;
error = mmap_file(vma->vm_file, vma);
+ map->vm_file = vma->vm_file;
+
if (error) {
UNMAP_STATE(unmap, vmi, vma, vma->vm_start, vma->vm_end,
map->prev, map->next);
- fput(vma->vm_file);
- vma->vm_file = NULL;
+ if (map_same_file(map))
+ fput(map->vm_file);
+ vma->vm_file = NULL;
vma_iter_set(vmi, vma->vm_end);
/* Undo any partial mapping done by a device driver. */
unmap_region(&unmap);
@@ -2638,7 +2645,6 @@ static int __mmap_new_file_vma(struct mm
!vma_flags_test(&map->vma_flags, VMA_MAYWRITE_BIT) &&
vma_test(vma, VMA_MAYWRITE_BIT));
- map->file = vma->vm_file;
map->vma_flags = vma->flags;
return 0;
@@ -2646,7 +2652,7 @@ static int __mmap_new_file_vma(struct mm
static void map_set_anon(struct mmap_state *map)
{
- map->file = NULL;
+ map->vm_file = NULL;
map->vm_ops = NULL;
map->pgoff = map->addr >> PAGE_SHIFT;
}
@@ -2658,7 +2664,7 @@ static bool map_is_private(const struct
static bool map_is_anon(const struct mmap_state *map)
{
- return map_is_private(map) && !map->file;
+ return map_is_private(map) && !map->vm_file;
}
/*
@@ -2703,7 +2709,7 @@ static int __mmap_new_vma(struct mmap_st
}
/* Invoke callbacks. */
- if (map->file)
+ if (map->vm_file)
error = __mmap_new_file_vma(map, vma);
else if (!is_anon)
error = shmem_zero_setup(vma);
@@ -2812,10 +2818,14 @@ static int call_mmap_prepare(struct mmap
int err;
/* Invoke the hook. */
- err = vfs_mmap_prepare(map->file, desc);
+ err = vfs_mmap_prepare(map->vm_file, desc);
if (err)
return err;
+ /* Update first so file refcount tracked correctly. */
+ if (desc->vm_file != map->vm_file)
+ map->vm_file = desc->vm_file;
+
/* It's invalid for mmap_prepare hooks to clear vm_ops. */
if (!desc->vm_ops)
return -EINVAL;
@@ -2826,10 +2836,6 @@ static int call_mmap_prepare(struct mmap
/* Update fields permitted to be changed. */
map->pgoff = desc->pgoff;
- if (desc->vm_file != map->file) {
- map->file_doesnt_need_get = true;
- map->file = desc->vm_file;
- }
map->vma_flags = desc->vma_flags;
map->page_prot = desc->page_prot;
/* User-defined fields. */
@@ -2841,7 +2847,7 @@ static int call_mmap_prepare(struct mmap
* anonymous mappings. Rather than allowing these mappings to be odd
* outliers, simply make them truly anonymous.
*/
- if (map_is_private(map) && file_is_dev_zero(map->file))
+ if (map_is_private(map) && file_is_dev_zero(map->vm_file))
map_set_anon(map);
return 0;
@@ -2860,7 +2866,7 @@ static void set_vma_user_defined_fields(
*/
static bool can_set_ksm_flags_early(struct mmap_state *map)
{
- struct file *file = map->file;
+ struct file *file = map->vm_file;
/* Anonymous mappings have no driver which can change them. */
if (!file)
@@ -2883,6 +2889,20 @@ static bool can_set_ksm_flags_early(stru
return false;
}
+static void map_put_vm_file(struct mmap_state *map)
+{
+ /*
+ * An error occurred or the VMA was merged.
+ *
+ * If the file was changed by the driver (which is required to increment
+ * the replacement file's reference count), drop its reference count.
+ *
+ * On error, the caller always drops the original file regardless.
+ */
+ if (map->vm_file && !map_same_file(map))
+ fput(map->vm_file);
+}
+
static unsigned long __mmap_region(struct file *file, unsigned long addr,
unsigned long len, vma_flags_t vma_flags,
unsigned long pgoff, struct list_head *uf)
@@ -2937,7 +2957,10 @@ static unsigned long __mmap_region(struc
__mmap_complete(&map, vma);
- if (have_mmap_prepare && allocated_new) {
+ if (!allocated_new) {
+ /* Merged, so need to drop refcount. */
+ map_put_vm_file(&map);
+ } else if (have_mmap_prepare) {
error = mmap_action_complete(vma, &desc.action,
/*is_compat=*/false);
if (error)
@@ -2951,13 +2974,7 @@ unacct_error:
if (map.charged)
vm_unacct_memory(map.charged);
abort_munmap:
- /*
- * This indicates that .mmap_prepare has set a new file, differing from
- * desc->vm_file. But since we're aborting the operation, only the
- * original file will be cleaned up. Ensure we clean up both.
- */
- if (map.file_doesnt_need_get)
- fput(map.file);
+ map_put_vm_file(&map);
vms_abort_munmap_vmas(&map.vms, &map.mas_detach);
return error;
}
--- a/mm/vma.h~mm-vma-fix-mmap_prepare-file-handling-remove-file_doesnt_need_get
+++ a/mm/vma.h
@@ -394,8 +394,10 @@ static inline void compat_set_vma_from_d
/* Mutable fields. Populated with initial state. */
vma_set_pgoff(vma, desc->pgoff);
- if (desc->vm_file != vma->vm_file)
- vma_set_file(vma, desc->vm_file);
+ if (desc->vm_file != vma->vm_file) {
+ fput(vma->vm_file);
+ vma->vm_file = desc->vm_file;
+ }
vma->flags = desc->vma_flags;
vma->vm_page_prot = desc->page_prot;
_
Patches currently in -mm which might be from ljs@kernel.org are
mm-mremap-fix-locked_vm-leak-from-mremap_dontunmap-self-merge.patch
mm-mremap-fix-locked_vm-leak-by-splitting-vma-for-mremap_dontunmap.patch
drivers-char-mem-mmap-readonly-map_shared-dev-zero-correctly.patch
mm-vmpressure-remove-window-size-todo.patch
tools-testing-selftests-mm-add-missing-gitignore-entries.patch
mm-move-drivers-char-memc-to-mm-char-memc.patch
mm-implement-file_is_dev_zero-to-uniquely-identify-dev-zero.patch
mm-vma-only-permit-map_private-dev-zero-to-be-mapped-anonymous.patch
mm-vma-make-map_private-mapped-dev-zero-mappings-truly-anonymous.patch
tools-testing-vma-add-test-to-assert-map_private-dev-zero-is-anon.patch
tools-testing-selftests-mm-add-map_private-dev-zero-merge-tests.patch
mm-madvise-swap-in-cowd-map_private-file-mappings-on-madv_willneed.patch
mm-khugepaged-deposit-a-newly-allocated-page-table-on-collapse.patch
mm-enable-mmu_gather_rcu_table_free-for-most-2-level-architectures.patch
mm-enable-mmu_gather_rcu_table_free-for-mmu-riscv.patch
mm-enable-mmu_gather_rcu_table_free-for-mmu-arm.patch
mm-enable-mmu_gather_rcu_table_free-for-arc-microblaze-xtensa.patch
mm-enable-mmu_gather_rcu_table_free-for-sparc64.patch
mm-enable-mmu_gather_rcu_table_free-for-m68k-coldfire.patch
mm-enable-mmu_gather_rcu_table_free-for-sh-x2.patch
mm-enable-mmu_gather_rcu_table_free-for-m68k-motorola.patch
mm-enable-mmu_gather_rcu_table_free-for-sparc32.patch
mm-userland-pgtable-freeing-is-rcu-safe-now-remove-leftover-bits.patch
mm-change-the-contract-for-free_pgtables-update-docs.patch
mm-vma-fix-mmap_prepare-file-handling-remove-file_doesnt_need_get.patch
mm-vma-introduce-and-use-vma_can_merge.patch
mm-consistently-validate-vma-state-after-mmap-hooks.patch
mm-vma-ensure-mmap_prepare-doesnt-set-actions-on-a-mergeable-vma.patch
mm-make-map_kernel_pages_-internal-and-unexported.patch
mm-vma-tidy-up-map-kernel-pages-enum-values.patch
mm-add-mmap-action-for-discontiguous-kernel-page-mapping.patch
docs-filesystems-update-mmap_prepare-docs-for-discontig-kernel-pgs.patch
drivers-usb-mon-update-to-use-mmap_prepare-map-kernel-pages.patch
infiniband-update-hfi1-to-use-remap_vmalloc_range.patch
selinux-reject-writable-opens-of-policy-file-drop-mmap-shared-write-check.patch
alsa-pcm-use-vm_insert_page-to-map-pcm-status-page.patch
bpf-arena-mark-arena_map_mmap-mappings-vm_mixedmap.patch
mm-vma-add-vma_is_mm_managed-predicates.patch
mm-vma-only-allow-mmap-to-clear-vma_maywrite_bit-if-not-mm-managed.patch
mm-vma-add-and-use-vma__is_fixed_mapping.patch
scsi-sg-convert-mmap-hook-to-mmap_prepare-and-rework.patch
fbdev-defio-assert-fbinfo_virtfb-drop-vm_io-add-vm_mixedmap.patch
hsi-cmt_speech-convert-mmap-hook-to-mmap_prepare-refactor.patch
mm-gup-error-out-early-on-vma_mayread_bit-vmas.patch
uprobes-remove-vm_io-set-vm_mixedmap-for-mapped-kernel-pages.patch
mm-mlock-clear-vma_locked_mask-over-mmap-callback.patch
mm-mlock-eliminate-weird-vma_io_bit-abuse-and-simplify.patch
mm-vma-enforce-that-mm-managed-mappings-may-not-set-vma_io_bit.patch
mm-remove-vma_io_bit-check-in-vma_is_mm_managed.patch
mm-remove-hugetlb_inlineh.patch
mm-rename-is_vm_hugetlb_page-to-vma_is_hugetlb.patch
mm-drop-some-redundant-checks-around-hugetlb-vmas.patch
mm-madvise-update-is_valid_guard_vma-to-use-vma_can_merge.patch
mm-vma-introduce-vma_is_mm_backed.patch
mm-uffd-use-predicates-for-userfaultfd-checks.patch
mm-madvise-use-predicates-for-madvise-madv_dofork.patch
mm-eliminate-vma_special_flags-usage-when-hugetlb-explicitly-tested.patch
mm-eliminate-vma_special_flags-check-in-lru_gen_look_around.patch
mm-avoid-use-of-vma_special_flags-in-migrate_vma_setup.patch
mm-eliminate-vm_special-vma_special_flags.patch
fuse-dax-do-not-set-vm_mixedmap.patch
mm-huge_memory-remove-vma_is_special_huge.patch
mm-vma-const-ify-vma_assert_stabilised-and-associated-functions.patch
mm-implement-and-use-vma_has_anon_rmap-silence-kcsan.patch
mm-update-comments-to-refer-to-anon-rmap-rather-than-anon_vma.patch
mm-vma-dont-remove-vma-from-rmap-if-pgoff-unchanged.patch
^ permalink raw reply [flat|nested] 5+ messages in thread* + mm-vma-fix-mmap_prepare-file-handling-remove-file_doesnt_need_get.patch added to mm-unstable branch
@ 2026-09-17 21:23 Andrew Morton
0 siblings, 0 replies; 5+ messages in thread
From: Andrew Morton @ 2026-09-17 21:23 UTC (permalink / raw)
To: mm-commits, ljs, akpm
The patch titled
Subject: mm/vma: fix mmap_prepare file handling, remove file_doesnt_need_get
has been added to the -mm mm-unstable branch. Its filename is
mm-vma-fix-mmap_prepare-file-handling-remove-file_doesnt_need_get.patch
This patch will shortly appear at
https://git.kernel.org/pub/scm/linux/kernel/git/akpm/25-new.git/tree/patches/mm-vma-fix-mmap_prepare-file-handling-remove-file_doesnt_need_get.patch
This patch will later appear in the mm-unstable branch at
git://git.kernel.org/pub/scm/linux/kernel/git/akpm/mm
Before you just go and hit "reply", please:
a) Consider who else should be cc'ed
b) Prefer to cc a suitable mailing list as well
c) Ideally: find the original patch on the mailing list and do a
reply-to-all to that, adding suitable additional cc's
*** Remember to use Documentation/process/submit-checklist.rst when testing your code ***
The -mm tree is included into linux-next via various
branches at git://git.kernel.org/pub/scm/linux/kernel/git/akpm/mm
and is updated there most days
------------------------------------------------------
From: "Lorenzo Stoakes (ARM)" <ljs@kernel.org>
Subject: mm/vma: fix mmap_prepare file handling, remove file_doesnt_need_get
Date: Thu, 17 Sep 2026 17:22:10 +0100
Patch series "mm: make VMA flag semantics explicit, eliminate VM_SPECIAL",
v3.
The VM_SPECIAL / VMA_SPECIAL_FLAGS mask conflates several unrelated
properties:
* Is this kernel-owned, whether MMIO, kernel-allocated pages, or ordinary
pages a driver maps itself?
* Can it be expanded or merged?
* Is this a 'weird' case like mlock where migration might race and we
'have' to set invalid flags to notify?
* Is it another 'weird' case where we just want to stop GUP from touching
it?
Driver writers have often been confused about this, and who can blame
them?
It also interacts badly with the eternal edgecase known as hugetlb - which
sets VMA_DONTEXPAND_BIT but doesn't also want to be treated like a
'special' flag.
Another issue is that we cannot make sensible assumptions about flag use.
It's not possible to assume VMA_IO_BIT means iommu because drivers abuse
it and mlock abuses it.
Special is also an overloaded term in mm. VDSO and VVAR mappings are also
called 'special' but they're special in a... special way.
Sometimes things are called special that are a subset of VMA_SPECIAL_FLAGS
(VMA_PFNMAP_BIT and VMA_MIXEDMAP_BIT for instance when it comes to zapping
or vm_normal_folio()).
There's a specific kind of special for THP too, which considers PFN map,
mixed map 'special' but DAX not.
It's all rather a mess.
This series brings some order to things by both limiting what drivers can
do with VMA flags and switching to using predicates that describe
behaviour, not arbitrary flags.
It establishes the invariant that only kernel-owned mappings may set
VMA_IO_BIT or clear VMA_MAYWRITE_BIT in an mmap hook, enforcing this by
validating VMA state after every mmap and mmap_prepare hook.
It updates usbmon and sg to mmap_prepare in order to do so, adding a new
mmap action for mapping discontiguous kernel pages, and has hfi1 and the
ALSA PCM status page map their pages eagerly instead.
It also establishes the invariant that VMA_MIXEDMAP_BIT be set when
mapping kernel memory, something that is usually the case but happens not
to be for some users - specifically defio, cmt_speech, uprobes and the bpf
arena, all of which are updated to do the right thing.
It replaces VM_SPECIAL and arbitrary flag tests with predicates that say
what is actually being tested:
vma_is_kernel_owned() Does a driver or kernel code manage a VMA's
life cycle?
vma_is_fixed_mapping() Is the VMA not permitted to be expanded or
merged?
vma_is_persistent() Do bytes written to the VMA stay written, and
bytes read stay the same unless userland changes
them?
vma_can_merge() Can the VMA be merged with a compatible
neighbour?
vma_can_gup() Can GUP obtain pages from the VMA, i.e. is it
neither a PFN map nor memory-mapped I/O?
Remaining raw VMA_IO_BIT, VMA_PFNMAP_BIT and VMA_MIXEDMAP_BIT tests
scattered across mm are also converted to predicates where it makes sense
to do so.
And also the opportunity is taken to eliminate THP's vma_is_special_huge()
which was an existing source of confusion.
This patch (of 40):
The map->file_doesnt_need_get flag is confusing and the existing
implementation has holes.
Drivers are permitted to change the owning file of a mapping. If they do
so, they are required to take a reference on that file.
The mmap() operation which ultimately invokes __mmap_region() is
guaranteed to drop the refcount for the original file the mapping was made
under, but this is not true for the replaced file.
This has been addressed so far by tracking map->file_doesnt_need_get,
which is rather poorly named and unfortunately fails to correctly track
whether or not an additional put were needed in a number of cases.
Make life easier by removing this flag, and instead drop the reference for
both mmap_prepare and the deprecated mmap callback in a new function
put_map().
Track whether this needs to be done by aligning mmap_state with
vm_area_desc and store the original file in the map->file field, keeping
the updated file in map->vm_file.
In order to have the same behaviour for both types of hooks, only drop the
reference __mmap_new_file_vma() itself took in its error path, deferring
the replaced file's reference to put_map().
To make this work correctly, map->vm_file has to be updated before any
error handling, so update __mmap_new_file_vma() and call_mmap_prepare() to
set this field first.
Also when mmap_prepare() changes the file and is then merged, the
reference count also must be decremented, so update the logic to call
put_map() in this case too.
Also update __compat_vma_mmap() to manually perform this step for stacked
file systems using the compatibility layer, and update
compat_set_vma_from_desc() to replace vma_set_file() with a correct
refcount/file update.
No in-tree driver is impacted by the incorrect implementation of this
currently (no driver that does this is mergeable for one), so this does
not need to be a fix.
Link: https://lore.kernel.org/20260917-b4-mmap-prepare-vma-flag-sanify-v3-0-4583d8a23bca@kernel.org
Link: https://lore.kernel.org/20260917-b4-mmap-prepare-vma-flag-sanify-v3-1-4583d8a23bca@kernel.org
Signed-off-by: Lorenzo Stoakes (ARM) <ljs@kernel.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Cc: Albert Ou <aou@eecs.berkeley.edu>
Cc: Alexander Gordeev <agordeev@linux.ibm.com>
Cc: Alexei Starovoitov <ast@kernel.org>
Cc: Alistair Popple <apopple@nvidia.com>
Cc: Al Viro <viro@zeniv.linux.org.uk>
Cc: Andreas Larsson <andreas@gaisler.com>
Cc: Andrii Nakryiko <andrii@kernel.org>
Cc: "Aneesh Kumar K.V" <aneesh.kumar@kernel.org>
Cc: Anup Patel <anup@brainfault.org>
Cc: Arnaldo Carvalho de Melo <acme@kernel.org>
Cc: Arnd Bergmann <arnd@arndb.de>
Cc: Axel Rasmussen <axelrasmussen@google.com>
Cc: Baolin Wang <baolin.wang@linux.alibaba.com>
Cc: Baoquan He <baoquan.he@linux.dev>
Cc: Barry Song <baohua@kernel.org>
Cc: "Borislav Petkov (AMD)" <bp@alien8.de>
Cc: Byungchul Park <byungchul@sk.com>
Cc: Catalin Marinas <catalin.marinas@arm.com>
Cc: Chengming Zhou <chengming.zhou@linux.dev>
Cc: Chris Li <chrisl@kernel.org>
Cc: Christian Borntraeger <borntraeger@linux.ibm.com>
Cc: Christian Brauner <brauner@kernel.org>
Cc: Claudio Imbrenda <imbrenda@linux.ibm.com>
Cc: Dave Airlie <airlied@gmail.com>
Cc: Dave Hansen <dave.hansen@linux.intel.com>
Cc: David Hildenbrand <david@kernel.org>
Cc: David S. Miller <davem@davemloft.net>
Cc: Dennis Dalessandro <dennis.dalessandro@cornelisnetworks.com>
Cc: Dev Jain <dev.jain@arm.com>
Cc: Doug Gilbert <dgilbert@interlog.com>
Cc: Eduard Zingerman <eddyz87@gmail.com>
Cc: Emil Tsalapatis (Meta) <emil@etsalapatis.com>
Cc: Gerald Schaefer <gerald.schaefer@linux.ibm.com>
Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Cc: Gregory Price <gourry@gourry.net>
Cc: Harry Yoo <harry@kernel.org>
Cc: Heiko Carstens <hca@linux.ibm.com>
Cc: Helge Deller <deller@gmx.de>
Cc: "Huang, Ying" <ying.huang@linux.alibaba.com>
Cc: Ingo Molnar <mingo@redhat.com>
Cc: James Bottomley <james.bottomley@HansenPartnership.com>
Cc: Jan Kara <jack@suse.cz>
Cc: Jann Horn <jannh@google.com>
Cc: Janosch Frank <frankja@linux.ibm.com>
Cc: Jaroslav Kysela <perex@perex.cz>
Cc: Jason Gunthorpe <jgg@ziepe.ca>
Cc: Jaya Kumar <jayalk@intworks.biz>
Cc: Johannes Weiner <hannes@cmpxchg.org>
Cc: John Hubbard <jhubbard@nvidia.com>
Cc: Jonathan Corbet <corbet@lwn.net>
Cc: Joshua Hahn <joshua.hahnjy@gmail.com>
Cc: Juri Lelli <juri.lelli@redhat.com>
Cc: Kairui Song <kasong@tencent.com>
Cc: Kemeng Shi <shikemeng@huaweicloud.com>
Cc: Kiryl Shutsemau <kas@kernel.org>
Cc: Kumar Kartikeya Dwivedi <memxor@gmail.com>
Cc: Lance Yang <lance.yang@linux.dev>
Cc: Leon Romanovsky <leon@kernel.org>
Cc: Liam R. Howlett <liam@infradead.org>
Cc: Maarten Lankhorst <maarten.lankhorst@linux.intel.com>
Cc: Madhavan Srinivasan <maddy@linux.ibm.com>
Cc: Marc Rutland <mark.rutland@arm.com>
Cc: Marc Zyngier <maz@kernel.org>
Cc: "Masami Hiramatsu (Google)" <mhiramat@kernel.org>
Cc: Matthew Brost <matthew.brost@intel.com>
Cc: Matthew Wilcox (Oracle) <willy@infradead.org>
Cc: Maxime Ripard <mripard@kernel.org>
Cc: Michal Hocko <mhocko@kernel.org>
Cc: Michal Hocko <mhocko@suse.com>
Cc: Mike Rapoport <rppt@kernel.org>
Cc: Miklos Szeredi <miklos@szeredi.hu>
Cc: Muchun Song <muchun.song@linux.dev>
Cc: Namhyung kim <namhyung@kernel.org>
Cc: Nhat Pham <nphamcs@gmail.com>
Cc: Nicholas Piggin <npiggin@gmail.com>
Cc: Oleg Nesterov <oleg@redhat.com>
Cc: Oscar Salvador <osalvador@suse.de>
Cc: Palmer Dabbelt <palmer@dabbelt.com>
Cc: Paul Moore <paul@paul-moore.com>
Cc: Pedro Falcato <pfalcato@suse.de>
Cc: Peter Xu <peterx@redhat.com>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Rakie Kim <rakie.kim@sk.com>
Cc: Rik van Riel <riel@surriel.com>
Cc: Ryan Roberts <ryan.roberts@arm.com>
Cc: Sebastian Reichel <sre@kernel.org>
Cc: Shakeel Butt <shakeel.butt@linux.dev>
Cc: Stephen Smalley <stephen.smalley.work@gmail.com>
Cc: Suren Baghdasaryan <surenb@google.com>
Cc: Takashi Iwai (SUSE) <tiwai@suse.de>
Cc: Takashi Iwai <tiwai@suse.com>
Cc: Thomas Zimemrmann <tzimmermann@suse.de>
Cc: Vasily Gorbik <gor@linux.ibm.com>
Cc: Vincent Guittot <vincent.guittot@linaro.org>
Cc: Vlastimil Babka <vbabka@kernel.org>
Cc: Wei Xu <weixugc@google.com>
Cc: Will Deacon <will@kernel.org>
Cc: Yuanchu Xie <yuanchu@google.com>
Cc: Zi Yan <ziy@nvidia.com>
---
mm/internal.h | 1
mm/util.c | 5 ++
mm/vma.c | 83 ++++++++++++++++++++++++++++--------------------
mm/vma.h | 6 ++-
4 files changed, 59 insertions(+), 36 deletions(-)
--- a/mm/internal.h~mm-vma-fix-mmap_prepare-file-handling-remove-file_doesnt_need_get
+++ a/mm/internal.h
@@ -7,6 +7,7 @@
#ifndef __MM_INTERNAL_H
#define __MM_INTERNAL_H
+#include <linux/file.h>
#include <linux/fs.h>
#include <linux/khugepaged.h>
#include <linux/mm.h>
--- a/mm/util.c~mm-vma-fix-mmap_prepare-file-handling-remove-file_doesnt_need_get
+++ a/mm/util.c
@@ -1228,8 +1228,11 @@ int __compat_vma_mmap(struct vm_area_des
/* Perform any preparatory tasks for mmap action. */
err = mmap_action_prepare(desc);
- if (err)
+ if (err) {
+ if (desc->vm_file != vma->vm_file)
+ fput(desc->vm_file);
return err;
+ }
/* Update the VMA from the descriptor. */
compat_set_vma_from_desc(vma, desc);
/* Complete any specified mmap actions. */
--- a/mm/vma.c~mm-vma-fix-mmap_prepare-file-handling-remove-file_doesnt_need_get
+++ a/mm/vma.c
@@ -24,7 +24,8 @@ struct mmap_state {
vm_flags_t vm_flags;
vma_flags_t vma_flags;
};
- struct file *file;
+ struct file *file; /* mmap()-specified file. */
+ struct file *vm_file; /* May be updated by mmap_prepare. */
pgprot_t page_prot;
/* User-defined fields, perhaps updated by .mmap_prepare(). */
@@ -43,8 +44,6 @@ struct mmap_state {
/* Determine if we can check KSM flags early in mmap() logic. */
bool check_ksm_early :1;
- /* If .mmap_prepare changed the file, we don't need to pin. */
- bool file_doesnt_need_get :1;
};
#define MMAP_STATE(name, mm_, vmi_, addr_, len_, pgoff_, anon_pgoff_, vma_flags_, file_) \
@@ -58,6 +57,7 @@ struct mmap_state {
.pglen = PHYS_PFN(len_), \
.vma_flags = vma_flags_, \
.file = file_, \
+ .vm_file = file_, \
.page_prot = vma_flags_to_page_prot(vma_flags_), \
}
@@ -70,7 +70,7 @@ struct mmap_state {
.vma_flags = (map_)->vma_flags, \
.pgoff = (map_)->pgoff, \
.anon_pgoff = (map_)->anon_pgoff, \
- .file = (map_)->file, \
+ .file = (map_)->vm_file, \
.prev = (map_)->prev, \
.middle = vma_, \
.next = (vma_) ? NULL : (map_)->next, \
@@ -2447,7 +2447,7 @@ void mm_drop_all_locks(struct mm_struct
*/
static bool accountable_mapping(struct mmap_state *map)
{
- const struct file *file = map->file;
+ const struct file *file = map->vm_file;
/*
* hugetlb has its own accounting separate from the core VM
@@ -2496,7 +2496,7 @@ static void vms_abort_munmap_vmas(struct
static void update_ksm_flags(struct mmap_state *map)
{
- map->vma_flags = ksm_vma_flags(map->mm, map->file, map->vma_flags);
+ map->vma_flags = ksm_vma_flags(map->mm, map->vm_file, map->vma_flags);
}
static void set_desc_from_map(struct vm_area_desc *desc,
@@ -2506,7 +2506,7 @@ static void set_desc_from_map(struct vm_
desc->end = map->end;
desc->pgoff = map->pgoff;
- desc->vm_file = map->file;
+ desc->vm_file = map->vm_file;
desc->vma_flags = map->vma_flags;
desc->page_prot = map->page_prot;
}
@@ -2586,6 +2586,10 @@ static int __mmap_setup(struct mmap_stat
return 0;
}
+static bool map_same_file(struct mmap_state *map)
+{
+ return map->vm_file == map->file;
+}
static int __mmap_new_file_vma(struct mmap_state *map,
struct vm_area_struct *vma)
@@ -2593,20 +2597,23 @@ static int __mmap_new_file_vma(struct mm
struct vma_iterator *vmi = map->vmi;
int error;
- vma->vm_file = map->file;
- if (!map->file_doesnt_need_get)
- get_file(map->file);
+ vma->vm_file = map->vm_file;
+ if (map_same_file(map))
+ get_file(map->vm_file);
- if (!map->file->f_op->mmap)
+ if (!map->vm_file->f_op->mmap)
return 0;
error = mmap_file(vma->vm_file, vma);
+ map->vm_file = vma->vm_file;
+
if (error) {
UNMAP_STATE(unmap, vmi, vma, vma->vm_start, vma->vm_end,
map->prev, map->next);
- fput(vma->vm_file);
- vma->vm_file = NULL;
+ if (map_same_file(map))
+ fput(map->vm_file);
+ vma->vm_file = NULL;
vma_iter_set(vmi, vma->vm_end);
/* Undo any partial mapping done by a device driver. */
unmap_region(&unmap);
@@ -2623,7 +2630,6 @@ static int __mmap_new_file_vma(struct mm
!vma_flags_test(&map->vma_flags, VMA_MAYWRITE_BIT) &&
vma_test(vma, VMA_MAYWRITE_BIT));
- map->file = vma->vm_file;
map->vma_flags = vma->flags;
return 0;
@@ -2631,7 +2637,7 @@ static int __mmap_new_file_vma(struct mm
static void map_set_anon(struct mmap_state *map)
{
- map->file = NULL;
+ map->vm_file = NULL;
map->vm_ops = NULL;
map->pgoff = map->addr >> PAGE_SHIFT;
}
@@ -2643,7 +2649,7 @@ static bool map_is_private(const struct
static bool map_is_anon(const struct mmap_state *map)
{
- return map_is_private(map) && !map->file;
+ return map_is_private(map) && !map->vm_file;
}
/*
@@ -2688,7 +2694,7 @@ static int __mmap_new_vma(struct mmap_st
}
/* Invoke callbacks. */
- if (map->file)
+ if (map->vm_file)
error = __mmap_new_file_vma(map, vma);
else if (!is_anon)
error = shmem_zero_setup(vma);
@@ -2797,11 +2803,15 @@ static int call_mmap_prepare(struct mmap
int err;
/* Invoke the hook. */
- err = vfs_mmap_prepare(map->file, desc);
+ err = vfs_mmap_prepare(map->vm_file, desc);
if (err)
return err;
- /* It's invalid for mmap_preprare hooks to clear vm_ops. */
+ /* Update first so file refcount tracked correctly. */
+ if (desc->vm_file != map->vm_file)
+ map->vm_file = desc->vm_file;
+
+ /* It's invalid for mmap_prepare hooks to clear vm_ops. */
if (!desc->vm_ops)
return -EINVAL;
@@ -2811,10 +2821,6 @@ static int call_mmap_prepare(struct mmap
/* Update fields permitted to be changed. */
map->pgoff = desc->pgoff;
- if (desc->vm_file != map->file) {
- map->file_doesnt_need_get = true;
- map->file = desc->vm_file;
- }
map->vma_flags = desc->vma_flags;
map->page_prot = desc->page_prot;
/* User-defined fields. */
@@ -2826,7 +2832,7 @@ static int call_mmap_prepare(struct mmap
* anonymous mappings. Rather than allowing these mappings to be odd
* outliers, simply make them truly anonymous.
*/
- if (map_is_private(map) && file_is_dev_zero(map->file))
+ if (map_is_private(map) && file_is_dev_zero(map->vm_file))
map_set_anon(map);
return 0;
@@ -2845,7 +2851,7 @@ static void set_vma_user_defined_fields(
*/
static bool can_set_ksm_flags_early(struct mmap_state *map)
{
- struct file *file = map->file;
+ struct file *file = map->vm_file;
/* Anonymous mappings have no driver which can change them. */
if (!file)
@@ -2868,6 +2874,20 @@ static bool can_set_ksm_flags_early(stru
return false;
}
+static void put_map(struct mmap_state *map)
+{
+ /*
+ * An error occurred or the VMA was merged.
+ *
+ * If the file was changed by the driver (which is required to increment
+ * the replacement file's reference count), drop its reference count.
+ *
+ * On error, the caller always drops the original file regardless.
+ */
+ if (map->vm_file && !map_same_file(map))
+ fput(map->vm_file);
+}
+
static unsigned long __mmap_region(struct file *file, unsigned long addr,
unsigned long len, vma_flags_t vma_flags,
unsigned long pgoff, struct list_head *uf)
@@ -2922,7 +2942,10 @@ static unsigned long __mmap_region(struc
__mmap_complete(&map, vma);
- if (have_mmap_prepare && allocated_new) {
+ if (!allocated_new) {
+ /* Merged, so need to drop refcount. */
+ put_map(&map);
+ } else if (have_mmap_prepare) {
error = mmap_action_complete(vma, &desc.action,
/*is_compat=*/false);
if (error)
@@ -2936,13 +2959,7 @@ unacct_error:
if (map.charged)
vm_unacct_memory(map.charged);
abort_munmap:
- /*
- * This indicates that .mmap_prepare has set a new file, differing from
- * desc->vm_file. But since we're aborting the operation, only the
- * original file will be cleaned up. Ensure we clean up both.
- */
- if (map.file_doesnt_need_get)
- fput(map.file);
+ put_map(&map);
vms_abort_munmap_vmas(&map.vms, &map.mas_detach);
return error;
}
--- a/mm/vma.h~mm-vma-fix-mmap_prepare-file-handling-remove-file_doesnt_need_get
+++ a/mm/vma.h
@@ -394,8 +394,10 @@ static inline void compat_set_vma_from_d
/* Mutable fields. Populated with initial state. */
vma_set_pgoff(vma, desc->pgoff);
- if (desc->vm_file != vma->vm_file)
- vma_set_file(vma, desc->vm_file);
+ if (desc->vm_file != vma->vm_file) {
+ fput(vma->vm_file);
+ vma->vm_file = desc->vm_file;
+ }
vma->flags = desc->vma_flags;
vma->vm_page_prot = desc->page_prot;
_
Patches currently in -mm which might be from ljs@kernel.org are
mm-vmpressure-remove-window-size-todo.patch
tools-testing-selftests-mm-add-missing-gitignore-entries.patch
mm-move-drivers-char-memc-to-mm-char-memc.patch
mm-implement-file_is_dev_zero-to-uniquely-identify-dev-zero.patch
mm-vma-only-permit-map_private-dev-zero-to-be-mapped-anonymous.patch
mm-vma-make-map_private-mapped-dev-zero-mappings-truly-anonymous.patch
tools-testing-vma-add-test-to-assert-map_private-dev-zero-is-anon.patch
tools-testing-selftests-mm-add-map_private-dev-zero-merge-tests.patch
mm-madvise-swap-in-cowd-map_private-file-mappings-on-madv_willneed.patch
mm-huge_memory-zap-deposited-page-tables-after-an-rcu-grace-period.patch
mm-enable-mmu_gather_rcu_table_free-for-most-2-level-architectures.patch
mm-enable-mmu_gather_rcu_table_free-for-mmu-riscv.patch
mm-enable-mmu_gather_rcu_table_free-for-mmu-arm.patch
mm-enable-mmu_gather_rcu_table_free-for-arc-microblaze-xtensa.patch
mm-enable-mmu_gather_rcu_table_free-for-sparc64.patch
mm-enable-mmu_gather_rcu_table_free-for-m68k-coldfire.patch
mm-enable-mmu_gather_rcu_table_free-for-sh-x2.patch
mm-enable-mmu_gather_rcu_table_free-for-m68k-motorola.patch
mm-enable-mmu_gather_rcu_table_free-for-sparc32.patch
mm-make-userland-page-table-freeing-rcu-safe.patch
mm-change-the-contract-for-free_pgtables-update-docs.patch
mm-vma-fix-mmap_prepare-file-handling-remove-file_doesnt_need_get.patch
mm-vma-predicate-setting-mmap_prepare-vma-fields-on-new-vma-alloc.patch
mm-vma-introduce-and-use-vma_can_merge.patch
mm-consistently-validate-vma-state-after-mmap-hooks.patch
mm-vma-ensure-mmap_prepare-doesnt-set-actions-on-a-mergeable-vma.patch
mm-make-map_kernel_pages_-internal-and-unexported.patch
mm-vma-tidy-up-map-kernel-pages-enum-values.patch
mm-add-mmap-action-for-discontiguous-kernel-page-mapping.patch
docs-filesystems-update-mmap_prepare-docs-for-discontig-kernel-pgs.patch
drivers-usb-mon-update-to-use-mmap_prepare-map-kernel-pages.patch
infiniband-update-hfi1-to-use-remap_vmalloc_range.patch
selinux-reject-writable-opens-of-policy-file-drop-mmap-shared-write-check.patch
alsa-pcm-use-vm_insert_page-to-map-pcm-status-page.patch
bpf-arena-mark-arena_map_mmap-mappings-vm_mixedmap.patch
mm-vma-add-vma_is_kernel_owned-predicates.patch
mm-vma-only-allow-mmap-to-clear-vma_maywrite_bit-if-kernel-owned.patch
mm-vma-add-and-use-vma__is_fixed_mapping.patch
scsi-sg-convert-mmap-hook-to-mmap_prepare-and-rework.patch
fbdev-defio-assert-fbinfo_virtfb-drop-vm_io-add-vm_mixedmap.patch
hsi-cmt_speech-convert-mmap-hook-to-mmap_prepare-refactor.patch
mm-gup-error-out-early-on-vma_mayread_bit-vmas.patch
uprobes-remove-vm_io-set-vm_mixedmap-for-mapped-kernel-pages.patch
mm-mlock-clear-vma_locked_mask-over-mmap-callback.patch
mm-mlock-eliminate-weird-vma_io_bit-abuse-and-simplify.patch
mm-vma-enforce-that-only-kernel-owned-mappings-may-set-vma_io_bit.patch
mm-remove-vma_io_bit-check-in-vma_is_kernel_owned.patch
mm-remove-hugetlb_inlineh.patch
mm-rename-is_vm_hugetlb_page-to-vma_is_hugetlb.patch
mm-drop-some-redundant-checks-around-hugetlb-vmas.patch
mm-madvise-update-is_valid_guard_vma-to-use-vma_can_merge.patch
mm-vma-introduce-vma_is_persistent.patch
mm-uffd-use-predicates-for-userfaultfd-checks.patch
mm-madvise-use-predicates-for-madvise-madv_dofork.patch
mm-eliminate-vma_special_flags-usage-when-hugetlb-explicitly-tested.patch
mm-eliminate-vma_special_flags-check-in-lru_gen_look_around.patch
mm-avoid-use-of-vma_special_flags-in-migrate_vma_setup.patch
mm-eliminate-vm_special-vma_special_flags.patch
fuse-dax-do-not-set-vm_mixedmap.patch
mm-huge_memory-remove-vma_is_special_huge.patch
mm-vma-introduce-and-use-vma_can_gup.patch
mm-vma-const-ify-vma_assert_stabilised-and-associated-functions.patch
mm-implement-and-use-vma_has_anon_rmap-silence-kcsan.patch
mm-update-comments-to-refer-to-anon-rmap-rather-than-anon_vma.patch
^ permalink raw reply [flat|nested] 5+ messages in thread
end of thread, other threads:[~2026-10-13 18:51 UTC | newest]
Thread overview: 5+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-10-10 22:01 + mm-vma-fix-mmap_prepare-file-handling-remove-file_doesnt_need_get.patch added to mm-unstable branch Andrew Morton
-- strict thread matches above, loose matches on Subject: below --
2026-10-13 18:51 Andrew Morton
2026-10-03 23:58 Andrew Morton
2026-10-03 23:34 Andrew Morton
2026-09-17 21:23 Andrew Morton
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.