[PATCH] mm: move security_file_mmap() back into do_mmap()

Lorenzo Stoakes lorenzo.stoakes at oracle.com
Wed Sep 25 08:44:40 UTC 2024


TL;DR: NACK because you sent two conflicting non-RFC patches as
'alternatives', which is not how development on-list works. Please resend
maybe one of these as an RFC...

+Al into this as he is the author of commit 8b3ec6814c83.


On Wed, Sep 25, 2024 at 04:16:28PM GMT, Shu Han wrote:
> This patch moves the security_file_mmap() back into do_mmap(), which
> revert the commit 8b3ec6814c83d76b85bd13badc48552836c24839
> ("take security_mmap_file() outside of ->mmap_sem"). Below is the reason.

Nit - we typically use the short version of the commit hash when
referencing a commit, so this would be commit 8b3ec6814c83.

Also if this is a revert this should be reflected in the subject... and
presumably given the original patch is from 2012 it's not a revert at all?

>
> Some logic may call do_mmap() without calling security_file_mmap(),
> without being aware of the harm this poses to LSM.

'Some logic'? Which?

There's no security_file_mmap() function in the kernel? You mean
security_mmap_file()? You're attempting to do something pretty major here,
so while this is obviously a typo it's pretty important you get these
details right :)

>
> For example, CVE-2016-10044[1] has reported many years ago, but the
> remap_file_pages() can still bypass the W^X policy enforced by SELinux[2]
> for a long time.
>
> Add a check is easy, but there may have more calls to do_mmap() in the
> future. Moving security_file_mmap() back into do_mmap() can avoid
> forgetting, and avoid repeated logic for whether READ_IMPLIES_EXEC should
> add PROT_EXEC for the mapping or not(In current, the !MMU case won't
> imply exec if the file's mmap_capabilities is not exist, but the
> security check logic is different).

But we might want to do things internal to the kernel that don't require
this check? Drivers can map too - the only place we need to be doing the
security check is in the user-facing mmap syscall.

If something is added that calls do_mmap() without proper security checks -
that's a bug in _that_ interface.

So I disagree with this patch as a whole.

Keep in mind we _do_ perform a security-hooked free memory check in the
mmap logic security_vm_enough_memory_mm(), so it isn't as if everything is
bypassed, only this security_mmap_file() function.

Which is surely only applicable in instances of user-facing API so is... in
the right place now?

Yeah I am not convinced on any level.

>
> It is noteworthy that moving the security check in do_mmap() will let it
> in the mmap_write_lock, which slows down the performance and even have
> deadlocks if someone depends on it(Since security_file_mprotect() is
> already in the lock, this possibility is tiny).

Err what? We can't accept a patch that might deadlock even if you claim the
possibility is 'tiny'... that's a NACK in itself. You _have_ to demonstrate
that you aren't deadlocking, this isn't optional.

>
> Link: https://project-zero.issues.chromium.org/issues/42452389 [1]
> Link: https://lore.kernel.org/all/20240919080905.4506-2-paul@paul-moore.com/ [2]
> Signed-off-by: Shu Han <ebpqwerty472123 at gmail.com>

You need to have a fixes tag here for Al's patch presumably.

> ---
> An alternative method is moving the check of READ_IMPLIES_EXEC out of
> do_mmap() and calling the LSM hooks at the same time, which has better
> performance and compatibility but may introduce some complexity. It has
> been proposed in [3], which cannot be applied at the same time with
> this patch.
> Link: https://lore.kernel.org/all/20240925063034.169-1-ebpqwerty472123@gmail.com/ [3]

You can't send multiple conflicting non-RFC patches at once... that's
completely ridiculous.

NACK, and re-send one of these as an RFC perhaps referencing the other as
an alternative?

> ---
>  include/linux/security.h |  8 ++++----
>  ipc/shm.c                |  4 ----
>  mm/mmap.c                |  9 +++++----
>  mm/nommu.c               |  5 ++++-
>  mm/util.c                | 19 ++++++++-----------
>  security/security.c      | 41 ++++------------------------------------
>  6 files changed, 25 insertions(+), 61 deletions(-)
>
> diff --git a/include/linux/security.h b/include/linux/security.h
> index c37c32ebbdcd..e061bc9a0331 100644
> --- a/include/linux/security.h
> +++ b/include/linux/security.h
> @@ -423,8 +423,8 @@ void security_file_free(struct file *file);
>  int security_file_ioctl(struct file *file, unsigned int cmd, unsigned long arg);
>  int security_file_ioctl_compat(struct file *file, unsigned int cmd,
>  			       unsigned long arg);
> -int security_mmap_file(struct file *file, unsigned long prot,
> -			unsigned long flags);
> +int security_mmap_file(struct file *file, unsigned long reqprot,
> +		       unsigned long prot, unsigned long flags);
>  int security_mmap_addr(unsigned long addr);
>  int security_file_mprotect(struct vm_area_struct *vma, unsigned long reqprot,
>  			   unsigned long prot);
> @@ -1077,8 +1077,8 @@ static inline int security_file_ioctl_compat(struct file *file,
>  	return 0;
>  }
>
> -static inline int security_mmap_file(struct file *file, unsigned long prot,
> -				     unsigned long flags)
> +static inline int security_mmap_file(struct file *file, unsigned long reqprot,
> +				     unsigned long prot, unsigned long flags)
>  {
>  	return 0;
>  }
> diff --git a/ipc/shm.c b/ipc/shm.c
> index 3e3071252dac..ce02560b856f 100644
> --- a/ipc/shm.c
> +++ b/ipc/shm.c
> @@ -1636,10 +1636,6 @@ long do_shmat(int shmid, char __user *shmaddr, int shmflg,
>  	sfd->vm_ops = NULL;
>  	file->private_data = sfd;
>
> -	err = security_mmap_file(file, prot, flags);
> -	if (err)
> -		goto out_fput;
> -
>  	if (mmap_write_lock_killable(current->mm)) {
>  		err = -EINTR;
>  		goto out_fput;
> diff --git a/mm/mmap.c b/mm/mmap.c
> index 18fddcce03b8..56f9520f85ab 100644
> --- a/mm/mmap.c
> +++ b/mm/mmap.c
> @@ -1260,6 +1260,7 @@ unsigned long do_mmap(struct file *file, unsigned long addr,
>  {
>  	struct mm_struct *mm = current->mm;
>  	int pkey = 0;
> +	unsigned long reqprot = prot, err;
>
>  	*populate = 0;
>
> @@ -1276,6 +1277,10 @@ unsigned long do_mmap(struct file *file, unsigned long addr,
>  		if (!(file && path_noexec(&file->f_path)))
>  			prot |= PROT_EXEC;
>
> +	err = security_mmap_file(file, reqprot, prot, flags);
> +	if (err)
> +		return err;
> +
>  	/* force arch specific MAP_FIXED handling in get_unmapped_area */
>  	if (flags & MAP_FIXED_NOREPLACE)
>  		flags |= MAP_FIXED;
> @@ -3198,12 +3203,8 @@ SYSCALL_DEFINE5(remap_file_pages, unsigned long, start, unsigned long, size,
>  		flags |= MAP_LOCKED;
>
>  	file = get_file(vma->vm_file);
> -	ret = security_mmap_file(vma->vm_file, prot, flags);
> -	if (ret)
> -		goto out_fput;
>  	ret = do_mmap(vma->vm_file, start, size,
>  			prot, flags, 0, pgoff, &populate, NULL);
> -out_fput:
>  	fput(file);
>  out:
>  	mmap_write_unlock(mm);
> diff --git a/mm/nommu.c b/mm/nommu.c
> index 7296e775e04e..e632f3105a5a 100644
> --- a/mm/nommu.c
> +++ b/mm/nommu.c
> @@ -681,7 +681,7 @@ static int validate_mmap_request(struct file *file,
>  				 unsigned long pgoff,
>  				 unsigned long *_capabilities)
>  {
> -	unsigned long capabilities, rlen;
> +	unsigned long capabilities, rlen, reqprot = prot;
>  	int ret;
>
>  	/* do the simple checks first */
> @@ -818,6 +818,9 @@ static int validate_mmap_request(struct file *file,
>  	}
>
>  	/* allow the security API to have its say */
> +	ret = security_mmap_file(file, reqprot, prot, flags);
> +	if (ret < 0)
> +		return ret;
>  	ret = security_mmap_addr(addr);
>  	if (ret < 0)
>  		return ret;
> diff --git a/mm/util.c b/mm/util.c
> index bd283e2132e0..47345e927a8f 100644
> --- a/mm/util.c
> +++ b/mm/util.c
> @@ -581,17 +581,14 @@ unsigned long vm_mmap_pgoff(struct file *file, unsigned long addr,
>  	unsigned long populate;
>  	LIST_HEAD(uf);
>
> -	ret = security_mmap_file(file, prot, flag);
> -	if (!ret) {
> -		if (mmap_write_lock_killable(mm))
> -			return -EINTR;
> -		ret = do_mmap(file, addr, len, prot, flag, 0, pgoff, &populate,
> -			      &uf);
> -		mmap_write_unlock(mm);
> -		userfaultfd_unmap_complete(mm, &uf);
> -		if (populate)
> -			mm_populate(ret, populate);
> -	}
> +	if (mmap_write_lock_killable(mm))
> +		return -EINTR;
> +	ret = do_mmap(file, addr, len, prot, flag, 0, pgoff, &populate,
> +		      &uf);
> +	mmap_write_unlock(mm);
> +	userfaultfd_unmap_complete(mm, &uf);
> +	if (populate)
> +		mm_populate(ret, populate);
>  	return ret;
>  }
>
> diff --git a/security/security.c b/security/security.c
> index 4564a0a1e4ef..25556629f588 100644
> --- a/security/security.c
> +++ b/security/security.c
> @@ -2927,42 +2927,10 @@ int security_file_ioctl_compat(struct file *file, unsigned int cmd,
>  }
>  EXPORT_SYMBOL_GPL(security_file_ioctl_compat);
>
> -static inline unsigned long mmap_prot(struct file *file, unsigned long prot)
> -{
> -	/*
> -	 * Does we have PROT_READ and does the application expect
> -	 * it to imply PROT_EXEC?  If not, nothing to talk about...
> -	 */
> -	if ((prot & (PROT_READ | PROT_EXEC)) != PROT_READ)
> -		return prot;
> -	if (!(current->personality & READ_IMPLIES_EXEC))
> -		return prot;
> -	/*
> -	 * if that's an anonymous mapping, let it.
> -	 */
> -	if (!file)
> -		return prot | PROT_EXEC;
> -	/*
> -	 * ditto if it's not on noexec mount, except that on !MMU we need
> -	 * NOMMU_MAP_EXEC (== VM_MAYEXEC) in this case
> -	 */
> -	if (!path_noexec(&file->f_path)) {
> -#ifndef CONFIG_MMU
> -		if (file->f_op->mmap_capabilities) {
> -			unsigned caps = file->f_op->mmap_capabilities(file);
> -			if (!(caps & NOMMU_MAP_EXEC))
> -				return prot;
> -		}
> -#endif
> -		return prot | PROT_EXEC;
> -	}
> -	/* anything on noexec mount won't get PROT_EXEC */
> -	return prot;
> -}
> -

Err.... why are we removing all of this??

>  /**
>   * security_mmap_file() - Check if mmap'ing a file is allowed
>   * @file: file
> + * @reqprot: protection requested by user
>   * @prot: protection applied by the kernel
>   * @flags: flags
>   *
> @@ -2971,11 +2939,10 @@ static inline unsigned long mmap_prot(struct file *file, unsigned long prot)
>   *
>   * Return: Returns 0 if permission is granted.
>   */
> -int security_mmap_file(struct file *file, unsigned long prot,
> -		       unsigned long flags)
> +int security_mmap_file(struct file *file, unsigned long reqprot,
> +		       unsigned long prot, unsigned long flags)
>  {
> -	return call_int_hook(mmap_file, file, prot, mmap_prot(file, prot),
> -			     flags);
> +	return call_int_hook(mmap_file, file, reqprot, prot, flags);
>  }
>
>  /**
>
> base-commit: f89722faa31466ff41aed21bdeb9cf34c2312858
> --
> 2.34.1
>



More information about the Linux-security-module-archive mailing list