From: "David Hildenbrand (Arm)" <david@kernel.org>
To: "Lorenzo Stoakes (ARM)" <ljs@kernel.org>,
Arnd Bergmann <arnd@arndb.de>,
Greg Kroah-Hartman <gregkh@linuxfoundation.org>,
Andrew Morton <akpm@linux-foundation.org>,
"Liam R. Howlett" <liam@infradead.org>,
Vlastimil Babka <vbabka@kernel.org>, Jann Horn <jannh@google.com>,
Pedro Falcato <pfalcato@suse.de>, Mike Rapoport <rppt@kernel.org>,
Suren Baghdasaryan <surenb@google.com>,
Michal Hocko <mhocko@suse.com>, Hugh Dickins <hughd@google.com>,
Baolin Wang <baolin.wang@linux.alibaba.com>,
"Matthew Wilcox (Oracle)" <willy@infradead.org>,
Jan Kara <jack@suse.cz>
Cc: linux-kernel@vger.kernel.org, linux-mm@kvack.org,
linux-fsdevel@vger.kernel.org, linux-kselftest@vger.kernel.org
Subject: Re: [PATCH 3/6] mm/vma: only permit MAP_PRIVATE /dev/zero to be mapped anonymous
Date: Mon, 7 Sep 2026 18:56:51 +0200 [thread overview]
Message-ID: <d6dc52e3-b27d-4663-848c-a2f270fb4c5d@kernel.org> (raw)
In-Reply-To: <20260902-map-private-dev-zero-v1-3-a578c730cec7@kernel.org>
On 9/2/26 20:00, Lorenzo Stoakes (ARM) wrote:
> In order to use mmap_prepare() with MAP_PRIVATE mappings of /dev/zero
> without the success_hook hack we explicitly permitted mmap_prepare handlers
> to set NULL vm_ops.
>
> However this is dangerous and we really only want to allow this for
> MAP_PRIVATE-mapped /dev/zero.
>
> Therefore use the newly introduced file_is_dev_zero() to uniquely identify
> MAP_PRIVATE-/dev/zero mappings and only permit this behaviour for them.
>
> Then, remove all ability for mmap_prepare or mmap hooks to set a VMA
> anonymous and update mmap_zero_prepare() to leave it to the core mmap code
> to do so.
>
> Note that this disallows nested MAP_PRIVATE-mappings of /dev/zero
> regions. Doing this would be broken in any case.
>
> We therefore do not need to update the mmap_prepare() compatibility layer
> to reflect these changes, as the mmap hook check suffices to disallow this
> behaviour.
>
> Now we're setting vma->vm_ops to NULL for an mmap_prepare-initialised
> MAP_PRIVATE-/dev/zero mapping, we have to avoid a subtle issue when
> updating user-defined fields via set_vma_user_defined_fields().
>
> The default for vma->vm_ops for all mmap_prepare-initialised mappings is
> vma_dummy_vm_ops, so map->vm_ops will be set to this and setting
> vma->vm_ops to this will render the VMA mistakenly non-anon.
>
> In general, we should never be setting user-defined fields for an anonymous
> VMA, so explicitly check for this to avoid doing so for the one case where
> a mapping can be both mmap_prepare and anonymous.
>
> In the case of legacy ->mmap hooks some drivers may set vma->vm_ops NULL
> believing this is the equivalent of setting no VMA operations. Therefore
> update mmap_file() to correct this by setting dummy VMA operations if this
> occurs.
>
> An example of this is drm_gem_shmem_mmap() which deliberately clears
> vma->vm_ops before handing the VMA to dma-buf. Cases such as this will be
> updated when they are converted to mmap_prepare.
>
> Also, in order to avoid a single commit bisection hazard, add a temporary
> workaround to set the VMA anonymous only after vma->vm_file is assigned in
> __mmap_new_file_vma().
>
> This is because vma_set_range() calls vma_set_pgoff() and
> assert_sane_pgoff() in turn, prior to the vma->vm_file being assigned. If
> we set the VMA anonymous early then this assert will fail.
>
> This is removed in the subsequent commit.
>
> Signed-off-by: Lorenzo Stoakes (ARM) <ljs@kernel.org>
> ---
> mm/char-mem.c | 6 +-----
> mm/internal.h | 17 ++++++++++-------
> mm/vma.c | 33 +++++++++++++++++++++++++--------
> 3 files changed, 36 insertions(+), 20 deletions(-)
>
> diff --git a/mm/char-mem.c b/mm/char-mem.c
> index e53e89e6ddd8..c0b5fb019223 100644
> --- a/mm/char-mem.c
> +++ b/mm/char-mem.c
> @@ -508,11 +508,7 @@ static int mmap_zero_prepare(struct vm_area_desc *desc)
> if (vma_desc_test(desc, VMA_SHARED_BIT))
> return shmem_zero_setup_desc(desc);
>
> - /*
> - * This is a highly unique situation where we mark a MAP_PRIVATE mapping
> - * of /dev/zero anonymous, despite it not being.
> - */
> - vma_desc_set_anonymous(desc);
> + /* MAP_PRIVATE semantics are taken care of for us by core mm. */
> return 0;
> }
>
> diff --git a/mm/internal.h b/mm/internal.h
> index 5d474e5f7709..da14c56fb24e 100644
> --- a/mm/internal.h
> +++ b/mm/internal.h
> @@ -226,15 +226,18 @@ static inline int mmap_file(struct file *file, struct vm_area_struct *vma)
> {
> int err = vfs_mmap(file, vma);
>
> - if (likely(!err))
> - return 0;
> -
> /*
> - * OK, we tried to call the file hook for mmap(), but an error
> - * arose. The mapping is in an inconsistent state and we must not invoke
> - * any further hooks on it.
> + * Either we tried to call the file hook for mmap() and an error arose
> + * or a driver set vma->vm_ops = NULL intending there to be no VMA
> + * operations.
> + *
> + * In the former case the VMA is in an inconsistent state and we mustn't
> + * invoke any further hooks on it, in the latter case the hook actually
> + * wanted no further hooks to be invoked, so fix both by setting dummy
> + * VMA ops.
> */
> - vma->vm_ops = &vma_dummy_vm_ops;
> + if (unlikely(err || !vma->vm_ops))
> + vma->vm_ops = &vma_dummy_vm_ops;
>
> return err;
> }
> diff --git a/mm/vma.c b/mm/vma.c
> index 35e7a64855fa..4b8d430d9619 100644
> --- a/mm/vma.c
> +++ b/mm/vma.c
> @@ -2621,6 +2621,19 @@ static int __mmap_new_file_vma(struct mmap_state *map,
> return 0;
> }
>
> +static bool map_is_private(const struct mmap_state *map)
> +{
> + return !vma_flags_test(&map->vma_flags, VMA_SHARED_BIT);
> +}
> +
> +static bool map_is_anon(const struct mmap_state *map)
I was wondering whether we should call this "map_is_private_anon", due to
MAP_ANON|MAP_SHARED. But looking at __mmap_new_vma(), the existing "is_anon" is
also limited to MAP_ANON|MAP_PRIVATE.
> +{
> + if (!map_is_private(map))
> + return false;
> +
> + return !map->file || file_is_dev_zero(map->file);
> +}
> +
> /*
> * __mmap_new_vma() - Allocate a new VMA for the region, as merging was not
> * possible.
> @@ -2634,8 +2647,7 @@ static int __mmap_new_file_vma(struct mmap_state *map,
> static int __mmap_new_vma(struct mmap_state *map, struct vm_area_struct **vmap,
> struct mmap_action *action)
> {
> - const bool is_anon = !map->file &&
> - !vma_flags_test(&map->vma_flags, VMA_SHARED_BIT);
> + const bool is_anon = map_is_anon(map);
> struct vma_iterator *vmi = map->vmi;
> int error = 0;
> struct vm_area_struct *vma;
> @@ -2651,7 +2663,7 @@ static int __mmap_new_vma(struct mmap_state *map, struct vm_area_struct **vmap,
>
> vma_iter_config(vmi, map->addr, map->end);
>
> - if (is_anon)
> + if (is_anon && !map->file)
> vma_set_anonymous(vma);
>
> vma_set_range(vma, map->addr, map->end, map->pgoff, map->anon_pgoff);
> @@ -2669,6 +2681,10 @@ static int __mmap_new_vma(struct mmap_state *map, struct vm_area_struct **vmap,
> else if (!is_anon)
> error = shmem_zero_setup(vma);
>
> + /* Temporary MAP_PRIVATE-/dev/zero workaround. */
> + if (is_anon && map->file)
> + vma_set_anonymous(vma);
> +
> if (error)
> goto free_iter_vma;
>
> @@ -2777,6 +2793,10 @@ static int call_mmap_prepare(struct mmap_state *map,
> if (err)
> return err;
>
> + /* Hooks cannot mark themselves anonymous. */
I guess this comment will be stale soon (after #4 where you drop the
set_anonymous part).
Should it be
"vm_ops are strictly required with mmap_prepare"
or sth like that?
> + if (!desc->vm_ops)
> + return -EINVAL;
> +
> err = call_action_prepare(map, desc);
> if (err)
> return err;
> @@ -2799,10 +2819,7 @@ static int call_mmap_prepare(struct mmap_state *map,
> static void set_vma_user_defined_fields(struct vm_area_struct *vma,
> struct mmap_state *map)
> {
> - if (map->vm_ops)
> - vma->vm_ops = map->vm_ops;
> - else /* Only /dev/zero should do this. */
> - vma_set_anonymous(vma);
> + vma->vm_ops = map->vm_ops;
> vma->vm_private_data = map->vm_private_data;
> }
>
> @@ -2882,7 +2899,7 @@ static unsigned long __mmap_region(struct file *file, unsigned long addr,
> allocated_new = true;
> }
>
> - if (have_mmap_prepare)
> + if (have_mmap_prepare && !map_is_anon(&map))
> set_vma_user_defined_fields(vma, &map);
Ah, we have mmap_zero_prepare() for handling the shmem_zero_setup_desc(). I was
just about to ask whether we can just get rid of this here.
But, hold on, do we now even need that? Could core-mm now take care of that as
well, and we could just remove mmap_zero_prepare() entirely?
That is, we'd make shmem_zero_setup() in __mmap_new_vma() take care of this?
Then we might not even need shmem_zero_setup_desc() anymore.
Maybe harder than it sounds at first.
--
Cheers,
David
next prev parent reply other threads:[~2026-09-07 16:56 UTC|newest]
Thread overview: 19+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-09-02 18:00 [PATCH 0/6] mm: make MAP_PRIVATE-/dev/zero mappings truly anonymous Lorenzo Stoakes (ARM)
2026-09-02 18:00 ` [PATCH 1/6] mm: move drivers/char/mem.c to mm/char-mem.c Lorenzo Stoakes (ARM)
2026-09-03 12:34 ` Mike Rapoport
2026-09-07 16:20 ` David Hildenbrand (Arm)
2026-09-02 18:00 ` [PATCH 2/6] mm: implement file_is_dev_zero() to uniquely identify /dev/zero Lorenzo Stoakes (ARM)
2026-09-07 16:21 ` David Hildenbrand (Arm)
2026-09-07 16:29 ` Lorenzo Stoakes (ARM)
2026-09-02 18:00 ` [PATCH 3/6] mm/vma: only permit MAP_PRIVATE /dev/zero to be mapped anonymous Lorenzo Stoakes (ARM)
2026-09-07 16:04 ` Gregory Price
2026-09-07 16:26 ` Lorenzo Stoakes (ARM)
2026-09-07 16:56 ` David Hildenbrand (Arm) [this message]
2026-09-07 17:38 ` Lorenzo Stoakes (ARM)
2026-09-07 19:54 ` David Hildenbrand (Arm)
2026-09-08 8:46 ` Lorenzo Stoakes (ARM)
2026-09-02 18:00 ` [PATCH 4/6] mm/vma: make MAP_PRIVATE-mapped /dev/zero mappings truly anonymous Lorenzo Stoakes (ARM)
2026-09-02 18:00 ` [PATCH 5/6] tools/testing/vma: add test to assert MAP_PRIVATE-/dev/zero is anon Lorenzo Stoakes (ARM)
2026-09-02 18:00 ` [PATCH 6/6] tools/testing/selftests/mm: add MAP_PRIVATE-/dev/zero merge tests Lorenzo Stoakes (ARM)
2026-09-07 17:08 ` David Hildenbrand (Arm)
2026-09-08 8:56 ` Lorenzo Stoakes (ARM)
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=d6dc52e3-b27d-4663-848c-a2f270fb4c5d@kernel.org \
--to=david@kernel.org \
--cc=akpm@linux-foundation.org \
--cc=arnd@arndb.de \
--cc=baolin.wang@linux.alibaba.com \
--cc=gregkh@linuxfoundation.org \
--cc=hughd@google.com \
--cc=jack@suse.cz \
--cc=jannh@google.com \
--cc=liam@infradead.org \
--cc=linux-fsdevel@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-kselftest@vger.kernel.org \
--cc=linux-mm@kvack.org \
--cc=ljs@kernel.org \
--cc=mhocko@suse.com \
--cc=pfalcato@suse.de \
--cc=rppt@kernel.org \
--cc=surenb@google.com \
--cc=vbabka@kernel.org \
--cc=willy@infradead.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®