On Wed 04-11-26 18:47:22, Christian Brauner wrote:
> Every procfs file has a ->mmap since commit 786d7e1612f0 ("Fix
> rmmod/read/write races in /proc entries"), whether the entry has a
> ->proc_mmap or not. On NOMMU validate_mmap_request() takes that to mean
> the file can be mapped. procfs has no ->mmap_capabilities so a regular
> file gets NOMMU_MAP_COPY, and a private mapping never calls ->mmap at
> all. Instead do_mmap() takes nommu_region_sem for writing and
> do_mmap_private() reads the file into the copy with kernel_read().
> 
> Reading /proc/maps takes nommu_region_sem as well:
> 
>   mmap(NULL, 4096, PROT_READ, MAP_PRIVATE, open("/proc/maps", O_RDONLY), 0)
> 
>   do_mmap()                          // down_write(&nommu_region_sem)
>   -> do_mmap_private()
>      -> kernel_read()
>         -> seq_read_iter()
>            -> nommu_region_list_start()   // down_read()
> 
> The task hangs for good, holding nommu_region_sem and its own mmap_lock,
> and every mmap(), munmap() and exit after that hangs behind it.
> /proc/maps is 0444 so any user can do that.
> 
> Give the procfs files a ->mmap_capabilities on NOMMU that allows
> nothing. That covers every entry and not just /proc/maps, procfs can't
> know which entry's read takes nommu_region_sem. Mapping a procfs file
> then fails with -ENODEV, like mapping a file without ->mmap did before
> 786d7e1612f0. NOMMU never called ->proc_mmap() for a regular file
> anyway, procfs never offered NOMMU_MAP_DIRECT.
> 
> Reported-by: Chris Mason <[email protected]>
> Fixes: 786d7e1612f0 ("Fix rmmod/read/write races in /proc entries")
> Cc: [email protected]
> Signed-off-by: Christian Brauner (Amutable) <[email protected]>

Looks good. Feel free to add:

Reviewed-by: Jan Kara <[email protected]>

                                                                Honza

> ---
>  fs/proc/inode.c | 24 ++++++++++++++++++++++++
>  1 file changed, 24 insertions(+)
> 
> diff --git a/fs/proc/inode.c b/fs/proc/inode.c
> index b7634f975d98..9933f03ab9fb 100644
> --- a/fs/proc/inode.c
> +++ b/fs/proc/inode.c
> @@ -466,6 +466,18 @@ proc_reg_get_unmapped_area(struct file *file, unsigned 
> long orig_addr,
>       return rv;
>  }
>  
> +#ifndef CONFIG_MMU
> +/*
> + * NOMMU never calls ->proc_mmap() for a regular file. It reads the file into
> + * a private copy instead, with nommu_region_sem held for writing, which
> + * reading /proc/maps takes as well. Don't map procfs files at all.
> + */
> +static unsigned int proc_reg_mmap_capabilities(struct file *file)
> +{
> +     return 0;
> +}
> +#endif
> +
>  static int proc_reg_open(struct inode *inode, struct file *file)
>  {
>       struct proc_dir_entry *pde = PDE(inode);
> @@ -559,6 +571,9 @@ static const struct file_operations proc_reg_file_ops = {
>       .unlocked_ioctl = proc_reg_unlocked_ioctl,
>       .mmap           = proc_reg_mmap,
>       .get_unmapped_area = proc_reg_get_unmapped_area,
> +#ifndef CONFIG_MMU
> +     .mmap_capabilities = proc_reg_mmap_capabilities,
> +#endif
>       .open           = proc_reg_open,
>       .release        = proc_reg_release,
>  };
> @@ -572,6 +587,9 @@ static const struct file_operations proc_iter_file_ops = {
>       .unlocked_ioctl = proc_reg_unlocked_ioctl,
>       .mmap           = proc_reg_mmap,
>       .get_unmapped_area = proc_reg_get_unmapped_area,
> +#ifndef CONFIG_MMU
> +     .mmap_capabilities = proc_reg_mmap_capabilities,
> +#endif
>       .open           = proc_reg_open,
>       .release        = proc_reg_release,
>  };
> @@ -586,6 +604,9 @@ static const struct file_operations 
> proc_reg_file_ops_compat = {
>       .compat_ioctl   = proc_reg_compat_ioctl,
>       .mmap           = proc_reg_mmap,
>       .get_unmapped_area = proc_reg_get_unmapped_area,
> +#ifndef CONFIG_MMU
> +     .mmap_capabilities = proc_reg_mmap_capabilities,
> +#endif
>       .open           = proc_reg_open,
>       .release        = proc_reg_release,
>  };
> @@ -600,6 +621,9 @@ static const struct file_operations 
> proc_iter_file_ops_compat = {
>       .compat_ioctl   = proc_reg_compat_ioctl,
>       .mmap           = proc_reg_mmap,
>       .get_unmapped_area = proc_reg_get_unmapped_area,
> +#ifndef CONFIG_MMU
> +     .mmap_capabilities = proc_reg_mmap_capabilities,
> +#endif
>       .open           = proc_reg_open,
>       .release        = proc_reg_release,
>  };
> 
> -- 
> 2.53.0
> 
-- 
Jan Kara <[email protected]>
SUSE Labs, CR

Reply via email to