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@xxxxxxxxxxxxxx/ [2] > Signed-off-by: Shu Han <ebpqwerty472123@xxxxxxxxx> 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@xxxxxxxxx/ [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 >