Re: [PATCH] ovl: fix mmap denywrite
From: Miklos Szeredi <miklos@szeredi.hu>
Date: 2021-07-09 13:48:35
Also in:
linux-fsdevel, linux-unionfs
On Wed, Jun 23, 2021 at 01:41:02PM +0200, Christian König wrote:
Am 22.06.21 um 17:10 schrieb Miklos Szeredi:quoted
On Tue, 22 Jun 2021 at 14:43, Christian König [off-list ref] wrote:quoted
Am 22.06.21 um 14:30 schrieb Miklos Szeredi:quoted
Overlayfs did not honor positive i_writecount on realfile for VM_DENYWRITE mappings. Similarly negative i_mmap_writable counts were ignored for VM_SHARED mappings. Fix by making vma_set_file() switch the temporary counts obtained and released by mmap_region().Mhm, I don't fully understand the background but that looks like something specific to overlayfs to me. So why are you changing the common helper?Need to hold the temporary counts until the final ones are obtained in vma_link(), which is out of overlayfs' scope.Ah! So basically we need to move the denial counts which mmap_region() added to the original file to the new one as well. That's indeed a rather good point. Can you rather change the vma_set_file() function to return the error and add a __must_check? I can take care fixing the users in DMA-buf and DRM subsystem.
Okay, but changing to __must_check has to be the last step to avoid compile errors. This v2 is with __must_check commented out. Thanks, Miklos --- From: Miklos Szeredi <redacted> Subject: [PATCH v2] ovl: fix mmap denywrite Overlayfs did not honor positive i_writecount on realfile for VM_DENYWRITE mappings. Similarly negative i_mmap_writable counts were ignored for VM_SHARED mappings. Fix by making vma_set_file() switch the temporary counts obtained and released by mmap_region(). Reported-by: Chengguang Xu <redacted> Signed-off-by: Miklos Szeredi <redacted> --- fs/overlayfs/file.c | 4 +++- include/linux/mm.h | 2 +- mm/mmap.c | 2 +- mm/util.c | 27 ++++++++++++++++++++++++++- 4 files changed, 31 insertions(+), 4 deletions(-)
--- a/fs/overlayfs/file.c
+++ b/fs/overlayfs/file.c@@ -430,7 +430,9 @@ static int ovl_mmap(struct file *file, s if (WARN_ON(file != vma->vm_file)) return -EIO; - vma_set_file(vma, realfile); + ret = vma_set_file(vma, realfile); + if (ret) + return ret; old_cred = ovl_override_creds(file_inode(file)->i_sb); ret = call_mmap(vma->vm_file, vma); --- a/include/linux/mm.h +++ b/include/linux/mm.h
@@ -2776,7 +2776,7 @@ static inline void vma_set_page_prot(str } #endif -void vma_set_file(struct vm_area_struct *vma, struct file *file); +int /* __must_check */ vma_set_file(struct vm_area_struct *vma, struct file *file); #ifdef CONFIG_NUMA_BALANCING unsigned long change_prot_numa(struct vm_area_struct *vma, --- a/mm/mmap.c +++ b/mm/mmap.c
@@ -1807,6 +1807,7 @@ unsigned long mmap_region(struct file *f */ vma->vm_file = get_file(file); error = call_mmap(file, vma); + file = vma->vm_file; if (error) goto unmap_and_free_vma;
@@ -1868,7 +1869,6 @@ unsigned long mmap_region(struct file *f if (vm_flags & VM_DENYWRITE) allow_write_access(file); } - file = vma->vm_file; out: perf_event_mmap(vma); --- a/mm/util.c +++ b/mm/util.c
@@ -314,12 +314,37 @@ int vma_is_stack_for_current(struct vm_a /* * Change backing file, only valid to use during initial VMA setup. */ -void vma_set_file(struct vm_area_struct *vma, struct file *file) +int vma_set_file(struct vm_area_struct *vma, struct file *file) { + vm_flags_t vm_flags = vma->vm_flags; + int err = 0; + /* Changing an anonymous vma with this is illegal */ get_file(file); + + /* Get temporary denial counts on replacement */ + if (vm_flags & VM_DENYWRITE) { + err = deny_write_access(file); + if (err) + goto out_put; + } + if (vm_flags & VM_SHARED) { + err = mapping_map_writable(file->f_mapping); + if (err) + goto out_allow; + } + swap(vma->vm_file, file); + + /* Undo temporary denial counts on replaced */ + if (vm_flags & VM_SHARED) + mapping_unmap_writable(file->f_mapping); +out_allow: + if (vm_flags & VM_DENYWRITE) + allow_write_access(file); +out_put: fput(file); + return err; } EXPORT_SYMBOL(vma_set_file);