On Mon, Jul 20, 2026 at 8:30 AM Lorenzo Stoakes (ARM) <[email protected]> wrote:
>
> When mapping /dev/zero with MAP_PRIVATE, one ends up with strange VMAs
> originating from Linux's distant past.
>
> These have vma->vm_file set but NULL vma->vm_ops, meaning they satisfy
> vma_is_anonymous() but otherwise resemble a file-backed VMA.
>
> The introduction of virtual page offsets and their subsequent use as
> indexes for MAP_PRIVATE-file-backed mappings mean the rmap does the right
> thing with these but we are left with inconsistencies.
>
> The vma_start_pgoff(vma) == vma_start_virt_pgoff(vma) invariant is true for
> all other anonymous VMAs, but not these.
>
> These VMAs are also observable as files in /proc/<pid>/[s]maps but
> otherwise behave like anonymous mappings.
>
> Therefore let's make these VMAs actually anonymous at mapping time which
> will activate the anonymous code path for mappings.
>
> This means we no longer have to account for this discrepancy anywhere and
> no longer have to think about these at all.

I'm glad to see this is solved finally. But /proc/<pid>/smaps will not
show /dev/zero anymore. It is user visible. I recalled you were
concerned about it before.
https://lore.kernel.org/linux-mm/[email protected]/

Anyway I don't think it is a big deal. It should be harmless. I would
be surprised any real life workload would care whether the anonymous
mapping is backed by /dev/zero or not in 2026.

Thanks,
Yang

>
> A previous commit gave us map_is_dev_zero() to positively identify these
> mappings, so we expressly only do so for these alone.
>
> The impact of this change should be low as likely very few are relying upon
> this in any case, and in using them are asking for anonymous memory, so no
> longer seeing these as file mappings in smaps should have no meaningful
> impact.
>
> Update assert_sane_pgoff(), the comment for vma_start_pgoff() and
> linear_virt_page_index() to reflect the change.
>
> We make this change in call_mmap_prepare() alone as /dev/zero has been
> converted to an mmap_prepare hook and we do not permit nested MAP_PRIVATE
> mapping of /dev/zero.
>
> We also remove the now defunct vma_desc_set_anonymous() and eliminate the
> temporary bisection hazard fix from the previous commit.
>
> Also update the VMA userland tests to reflect the change.
>
> Signed-off-by: Lorenzo Stoakes (ARM) <[email protected]>
> ---
>  include/linux/mm.h              | 10 ++--------
>  include/linux/pagemap.h         |  3 +--
>  mm/vma.c                        | 26 +++++++++++++++++---------
>  mm/vma.h                        |  3 ---
>  tools/testing/vma/include/dup.h |  3 +--
>  5 files changed, 21 insertions(+), 24 deletions(-)
>
> diff --git a/include/linux/mm.h b/include/linux/mm.h
> index 7614afe99c96..7fabe6c66b4b 100644
> --- a/include/linux/mm.h
> +++ b/include/linux/mm.h
> @@ -1554,11 +1554,6 @@ static inline void vma_set_anonymous(struct 
> vm_area_struct *vma)
>         vma->vm_ops = NULL;
>  }
>
> -static inline void vma_desc_set_anonymous(struct vm_area_desc *desc)
> -{
> -       desc->vm_ops = NULL;
> -}
> -
>  static inline bool vma_is_anonymous(const struct vm_area_struct *vma)
>  {
>         return !vma->vm_ops;
> @@ -4352,9 +4347,8 @@ static inline unsigned long vma_pages(const struct 
> vm_area_struct *vma)
>   * If @vma is a MAP_PRIVATE file-backed mapping, then this returns the
>   * page offset within the file.
>   *
> - * Edge cases: nommu does not abide by these, MAP_PRIVATE-/dev/zero satisfies
> - * vma_is_anonymous() but has file-backed page offset, and MAP_PRIVATE-pfnmap
> - * regions have their page offset set to the first PFN in the range.
> + * Edge cases: nommu does not abide by these and CoW MAP_PRIVATE-pfnmap 
> regions
> + * have their page offset set to the first PFN in the range.
>   *
>   * Returns: The page offset of the start of @vma.
>   */
> diff --git a/include/linux/pagemap.h b/include/linux/pagemap.h
> index 3307c0551a0a..4e8b2b29f6d3 100644
> --- a/include/linux/pagemap.h
> +++ b/include/linux/pagemap.h
> @@ -1136,8 +1136,7 @@ static inline pgoff_t linear_virt_page_index(const 
> struct vm_area_struct *vma,
>         const pgoff_t pgoff = __linear_virt_page_index(vma, address);
>
>         VM_WARN_ON_ONCE(vma_test(vma, VMA_SHARED_BIT));
> -       /* Account for MAP_PRIVATE-/dev/zero which is only semi-anonymous. */
> -       if (vma_is_anonymous(vma) && !vma->vm_file)
> +       if (vma_is_anonymous(vma))
>                 VM_WARN_ON_ONCE(pgoff != linear_page_index(vma, address));
>
>         return pgoff;
> diff --git a/mm/vma.c b/mm/vma.c
> index 103a6a134dec..d3e0457a11a1 100644
> --- a/mm/vma.c
> +++ b/mm/vma.c
> @@ -2622,6 +2622,13 @@ static bool map_is_dev_zero(const struct mmap_state 
> *map)
>         return imajor(inode) == MEM_MAJOR && iminor(inode) == DEVZERO_MINOR;
>  }
>
> +static void map_set_anon(struct mmap_state *map)
> +{
> +       map->file = NULL;
> +       map->vm_ops = NULL;
> +       map->pgoff = map->addr >> PAGE_SHIFT;
> +}
> +
>  static bool map_is_private(const struct mmap_state *map)
>  {
>         return !vma_flags_test(&map->vma_flags, VMA_SHARED_BIT);
> @@ -2629,10 +2636,7 @@ static bool map_is_private(const struct mmap_state 
> *map)
>
>  static bool map_is_anon(const struct mmap_state *map)
>  {
> -       if (!map_is_private(map))
> -               return false;
> -
> -       return !map->file || map_is_dev_zero(map);
> +       return map_is_private(map) && !map->file;
>  }
>
>  /*
> @@ -2664,7 +2668,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 && !map->file)
> +       if (is_anon)
>                 vma_set_anonymous(vma);
>
>         vma_set_range(vma, map->addr, map->end, map->pgoff, map->virt_pgoff);
> @@ -2682,10 +2686,6 @@ 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;
>
> @@ -2814,6 +2814,14 @@ static int call_mmap_prepare(struct mmap_state *map,
>         map->vm_ops = desc->vm_ops;
>         map->vm_private_data = desc->private_data;
>
> +       /*
> +        * MAP_PRIVATE-/dev/zero mappings are an ancient way of getting
> +        * anonymous mappings. Rather than allowing these mappings to be odd
> +        * outliers, simply make them truly anonymous.
> +        */
> +       if (map_is_private(map) && map_is_dev_zero(map))
> +               map_set_anon(map);
> +
>         return 0;
>  }
>
> diff --git a/mm/vma.h b/mm/vma.h
> index 94277837ce6e..44fbb50dc3fe 100644
> --- a/mm/vma.h
> +++ b/mm/vma.h
> @@ -267,9 +267,6 @@ static inline void assert_sane_pgoff(struct 
> vm_area_struct *vma, pgoff_t pgoff)
>          */
>         if (!vma_is_anonymous(vma))
>                 return;
> -       /* MAP_PRIVATE-/dev/zero is anon, non-NULL vm_file, but has file 
> pgoff. */
> -       if (vma->vm_file)
> -               return;
>         /* If faulted in, could have been remapped. */
>         if (vma->anon_vma)
>                 return;
> diff --git a/tools/testing/vma/include/dup.h b/tools/testing/vma/include/dup.h
> index c617ee2532bd..c5191b9718fd 100644
> --- a/tools/testing/vma/include/dup.h
> +++ b/tools/testing/vma/include/dup.h
> @@ -1648,8 +1648,7 @@ static inline pgoff_t linear_virt_page_index(const 
> struct vm_area_struct *vma,
>         const pgoff_t pgoff = __linear_virt_page_index(vma, address);
>
>         VM_WARN_ON_ONCE(vma_test(vma, VMA_SHARED_BIT));
> -       /* Account for MAP_PRIVATE-/dev/zero which is only semi-anonymous. */
> -       if (vma_is_anonymous(vma) && !vma->vm_file)
> +       if (vma_is_anonymous(vma))
>                 VM_WARN_ON_ONCE(pgoff != linear_page_index(vma, address));
>
>         return pgoff;
>
> --
> 2.55.0
>
>

Reply via email to