* [PATCH v4 1/3] mm: drop the assumption that VM_SHARED always implies writable
2023-10-12 17:04 [PATCH v4 0/3] permit write-sealed memfd read-only shared mappings Lorenzo Stoakes
@ 2023-10-12 17:04 ` Lorenzo Stoakes
2023-10-12 17:04 ` [PATCH v4 2/3] mm: update memfd seal write check to include F_SEAL_WRITE Lorenzo Stoakes
2023-10-12 17:04 ` [PATCH v4 3/3] mm: perform the mapping_map_writable() check after call_mmap() Lorenzo Stoakes
2 siblings, 0 replies; 6+ messages in thread
From: Lorenzo Stoakes @ 2023-10-12 17:04 UTC (permalink / raw)
To: linux-mm, linux-kernel, Andrew Morton
Cc: Mike Kravetz, Muchun Song, Alexander Viro, Christian Brauner,
Matthew Wilcox, Hugh Dickins, Andy Lutomirski, Jan Kara,
linux-fsdevel, bpf, Lorenzo Stoakes
There is a general assumption that VMAs with the VM_SHARED flag set are
writable. If the VM_MAYWRITE flag is not set, then this is simply not the
case.
Update those checks which affect the struct address_space->i_mmap_writable
field to explicitly test for this by introducing [vma_]is_shared_maywrite()
helper functions.
This remains entirely conservative, as the lack of VM_MAYWRITE guarantees
that the VMA cannot be written to.
Suggested-by: Andy Lutomirski <luto@kernel.org>
Signed-off-by: Lorenzo Stoakes <lstoakes@gmail.com>
---
include/linux/fs.h | 4 ++--
include/linux/mm.h | 11 +++++++++++
kernel/fork.c | 2 +-
mm/filemap.c | 2 +-
mm/madvise.c | 2 +-
mm/mmap.c | 12 ++++++------
6 files changed, 22 insertions(+), 11 deletions(-)
diff --git a/include/linux/fs.h b/include/linux/fs.h
index 92a9c6157de1..e9c03fb00d5c 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -454,7 +454,7 @@ extern const struct address_space_operations empty_aops;
* It is also used to block modification of page cache contents through
* memory mappings.
* @gfp_mask: Memory allocation flags to use for allocating pages.
- * @i_mmap_writable: Number of VM_SHARED mappings.
+ * @i_mmap_writable: Number of VM_SHARED, VM_MAYWRITE mappings.
* @nr_thps: Number of THPs in the pagecache (non-shmem only).
* @i_mmap: Tree of private and shared mappings.
* @i_mmap_rwsem: Protects @i_mmap and @i_mmap_writable.
@@ -557,7 +557,7 @@ static inline int mapping_mapped(struct address_space *mapping)
/*
* Might pages of this file have been modified in userspace?
- * Note that i_mmap_writable counts all VM_SHARED vmas: do_mmap
+ * Note that i_mmap_writable counts all VM_SHARED, VM_MAYWRITE vmas: do_mmap
* marks vma as VM_SHARED if it is shared, and the file was opened for
* writing i.e. vma may be mprotected writable even if now readonly.
*
diff --git a/include/linux/mm.h b/include/linux/mm.h
index 74d7547ffb70..bae234d18d81 100644
--- a/include/linux/mm.h
+++ b/include/linux/mm.h
@@ -937,6 +937,17 @@ static inline bool vma_is_accessible(struct vm_area_struct *vma)
return vma->vm_flags & VM_ACCESS_FLAGS;
}
+static inline bool is_shared_maywrite(vm_flags_t vm_flags)
+{
+ return (vm_flags & (VM_SHARED | VM_MAYWRITE)) ==
+ (VM_SHARED | VM_MAYWRITE);
+}
+
+static inline bool vma_is_shared_maywrite(struct vm_area_struct *vma)
+{
+ return is_shared_maywrite(vma->vm_flags);
+}
+
static inline
struct vm_area_struct *vma_find(struct vma_iterator *vmi, unsigned long max)
{
diff --git a/kernel/fork.c b/kernel/fork.c
index e45a4457ba83..1e6c656e0857 100644
--- a/kernel/fork.c
+++ b/kernel/fork.c
@@ -733,7 +733,7 @@ static __latent_entropy int dup_mmap(struct mm_struct *mm,
get_file(file);
i_mmap_lock_write(mapping);
- if (tmp->vm_flags & VM_SHARED)
+ if (vma_is_shared_maywrite(tmp))
mapping_allow_writable(mapping);
flush_dcache_mmap_lock(mapping);
/* insert tmp into the share list, just after mpnt */
diff --git a/mm/filemap.c b/mm/filemap.c
index 9ef49255f1a5..9710f43a89ac 100644
--- a/mm/filemap.c
+++ b/mm/filemap.c
@@ -3618,7 +3618,7 @@ int generic_file_mmap(struct file *file, struct vm_area_struct *vma)
*/
int generic_file_readonly_mmap(struct file *file, struct vm_area_struct *vma)
{
- if ((vma->vm_flags & VM_SHARED) && (vma->vm_flags & VM_MAYWRITE))
+ if (vma_is_shared_maywrite(vma))
return -EINVAL;
return generic_file_mmap(file, vma);
}
diff --git a/mm/madvise.c b/mm/madvise.c
index 70dafc99ff1e..6214a1ab5654 100644
--- a/mm/madvise.c
+++ b/mm/madvise.c
@@ -981,7 +981,7 @@ static long madvise_remove(struct vm_area_struct *vma,
return -EINVAL;
}
- if ((vma->vm_flags & (VM_SHARED|VM_WRITE)) != (VM_SHARED|VM_WRITE))
+ if (!vma_is_shared_maywrite(vma))
return -EACCES;
offset = (loff_t)(start - vma->vm_start)
diff --git a/mm/mmap.c b/mm/mmap.c
index 3ea52451623b..0041e3631f6c 100644
--- a/mm/mmap.c
+++ b/mm/mmap.c
@@ -107,7 +107,7 @@ void vma_set_page_prot(struct vm_area_struct *vma)
static void __remove_shared_vm_struct(struct vm_area_struct *vma,
struct file *file, struct address_space *mapping)
{
- if (vma->vm_flags & VM_SHARED)
+ if (vma_is_shared_maywrite(vma))
mapping_unmap_writable(mapping);
flush_dcache_mmap_lock(mapping);
@@ -384,7 +384,7 @@ static unsigned long count_vma_pages_range(struct mm_struct *mm,
static void __vma_link_file(struct vm_area_struct *vma,
struct address_space *mapping)
{
- if (vma->vm_flags & VM_SHARED)
+ if (vma_is_shared_maywrite(vma))
mapping_allow_writable(mapping);
flush_dcache_mmap_lock(mapping);
@@ -2846,7 +2846,7 @@ unsigned long mmap_region(struct file *file, unsigned long addr,
vma->vm_pgoff = pgoff;
if (file) {
- if (vm_flags & VM_SHARED) {
+ if (is_shared_maywrite(vm_flags)) {
error = mapping_map_writable(file->f_mapping);
if (error)
goto free_vma;
@@ -2920,7 +2920,7 @@ unsigned long mmap_region(struct file *file, unsigned long addr,
mm->map_count++;
if (vma->vm_file) {
i_mmap_lock_write(vma->vm_file->f_mapping);
- if (vma->vm_flags & VM_SHARED)
+ if (vma_is_shared_maywrite(vma))
mapping_allow_writable(vma->vm_file->f_mapping);
flush_dcache_mmap_lock(vma->vm_file->f_mapping);
@@ -2937,7 +2937,7 @@ unsigned long mmap_region(struct file *file, unsigned long addr,
/* Once vma denies write, undo our temporary denial count */
unmap_writable:
- if (file && vm_flags & VM_SHARED)
+ if (file && is_shared_maywrite(vm_flags))
mapping_unmap_writable(file->f_mapping);
file = vma->vm_file;
ksm_add_vma(vma);
@@ -2985,7 +2985,7 @@ unsigned long mmap_region(struct file *file, unsigned long addr,
unmap_region(mm, &vmi.mas, vma, prev, next, vma->vm_start,
vma->vm_end, vma->vm_end, true);
}
- if (file && (vm_flags & VM_SHARED))
+ if (file && is_shared_maywrite(vm_flags))
mapping_unmap_writable(file->f_mapping);
free_vma:
vm_area_free(vma);
--
2.42.0
^ permalink raw reply [flat|nested] 6+ messages in thread* [PATCH v4 2/3] mm: update memfd seal write check to include F_SEAL_WRITE
2023-10-12 17:04 [PATCH v4 0/3] permit write-sealed memfd read-only shared mappings Lorenzo Stoakes
2023-10-12 17:04 ` [PATCH v4 1/3] mm: drop the assumption that VM_SHARED always implies writable Lorenzo Stoakes
@ 2023-10-12 17:04 ` Lorenzo Stoakes
2023-10-13 10:32 ` Jan Kara
2023-10-12 17:04 ` [PATCH v4 3/3] mm: perform the mapping_map_writable() check after call_mmap() Lorenzo Stoakes
2 siblings, 1 reply; 6+ messages in thread
From: Lorenzo Stoakes @ 2023-10-12 17:04 UTC (permalink / raw)
To: linux-mm, linux-kernel, Andrew Morton
Cc: Mike Kravetz, Muchun Song, Alexander Viro, Christian Brauner,
Matthew Wilcox, Hugh Dickins, Andy Lutomirski, Jan Kara,
linux-fsdevel, bpf, Lorenzo Stoakes
The seal_check_future_write() function is called by shmem_mmap() or
hugetlbfs_file_mmap() to disallow any future writable mappings of an memfd
sealed this way.
The F_SEAL_WRITE flag is not checked here, as that is handled via the
mapping->i_mmap_writable mechanism and so any attempt at a mapping would
fail before this could be run.
However we intend to change this, meaning this check can be performed for
F_SEAL_WRITE mappings also.
The logic here is equally applicable to both flags, so update this function
to accommodate both and rename it accordingly.
Signed-off-by: Lorenzo Stoakes <lstoakes@gmail.com>
---
fs/hugetlbfs/inode.c | 2 +-
include/linux/mm.h | 15 ++++++++-------
mm/shmem.c | 2 +-
3 files changed, 10 insertions(+), 9 deletions(-)
diff --git a/fs/hugetlbfs/inode.c b/fs/hugetlbfs/inode.c
index 06693bb1153d..5c333373dcc9 100644
--- a/fs/hugetlbfs/inode.c
+++ b/fs/hugetlbfs/inode.c
@@ -112,7 +112,7 @@ static int hugetlbfs_file_mmap(struct file *file, struct vm_area_struct *vma)
vm_flags_set(vma, VM_HUGETLB | VM_DONTEXPAND);
vma->vm_ops = &hugetlb_vm_ops;
- ret = seal_check_future_write(info->seals, vma);
+ ret = seal_check_write(info->seals, vma);
if (ret)
return ret;
diff --git a/include/linux/mm.h b/include/linux/mm.h
index bae234d18d81..26d7dc3b342b 100644
--- a/include/linux/mm.h
+++ b/include/linux/mm.h
@@ -4078,25 +4078,26 @@ static inline void mem_dump_obj(void *object) {}
#endif
/**
- * seal_check_future_write - Check for F_SEAL_FUTURE_WRITE flag and handle it
+ * seal_check_write - Check for F_SEAL_WRITE or F_SEAL_FUTURE_WRITE flags and
+ * handle them.
* @seals: the seals to check
* @vma: the vma to operate on
*
- * Check whether F_SEAL_FUTURE_WRITE is set; if so, do proper check/handling on
- * the vma flags. Return 0 if check pass, or <0 for errors.
+ * Check whether F_SEAL_WRITE or F_SEAL_FUTURE_WRITE are set; if so, do proper
+ * check/handling on the vma flags. Return 0 if check pass, or <0 for errors.
*/
-static inline int seal_check_future_write(int seals, struct vm_area_struct *vma)
+static inline int seal_check_write(int seals, struct vm_area_struct *vma)
{
- if (seals & F_SEAL_FUTURE_WRITE) {
+ if (seals & (F_SEAL_WRITE | F_SEAL_FUTURE_WRITE)) {
/*
* New PROT_WRITE and MAP_SHARED mmaps are not allowed when
- * "future write" seal active.
+ * write seals are active.
*/
if ((vma->vm_flags & VM_SHARED) && (vma->vm_flags & VM_WRITE))
return -EPERM;
/*
- * Since an F_SEAL_FUTURE_WRITE sealed memfd can be mapped as
+ * Since an F_SEAL_[FUTURE_]WRITE sealed memfd can be mapped as
* MAP_SHARED and read-only, take care to not allow mprotect to
* revert protections on such mappings. Do this only for shared
* mappings. For private mappings, don't need to mask
diff --git a/mm/shmem.c b/mm/shmem.c
index 6503910b0f54..cab053831fea 100644
--- a/mm/shmem.c
+++ b/mm/shmem.c
@@ -2405,7 +2405,7 @@ static int shmem_mmap(struct file *file, struct vm_area_struct *vma)
struct shmem_inode_info *info = SHMEM_I(inode);
int ret;
- ret = seal_check_future_write(info->seals, vma);
+ ret = seal_check_write(info->seals, vma);
if (ret)
return ret;
--
2.42.0
^ permalink raw reply [flat|nested] 6+ messages in thread* Re: [PATCH v4 2/3] mm: update memfd seal write check to include F_SEAL_WRITE
2023-10-12 17:04 ` [PATCH v4 2/3] mm: update memfd seal write check to include F_SEAL_WRITE Lorenzo Stoakes
@ 2023-10-13 10:32 ` Jan Kara
0 siblings, 0 replies; 6+ messages in thread
From: Jan Kara @ 2023-10-13 10:32 UTC (permalink / raw)
To: Lorenzo Stoakes
Cc: linux-mm, linux-kernel, Andrew Morton, Mike Kravetz, Muchun Song,
Alexander Viro, Christian Brauner, Matthew Wilcox, Hugh Dickins,
Andy Lutomirski, Jan Kara, linux-fsdevel, bpf
On Thu 12-10-23 18:04:29, Lorenzo Stoakes wrote:
> The seal_check_future_write() function is called by shmem_mmap() or
> hugetlbfs_file_mmap() to disallow any future writable mappings of an memfd
> sealed this way.
>
> The F_SEAL_WRITE flag is not checked here, as that is handled via the
> mapping->i_mmap_writable mechanism and so any attempt at a mapping would
> fail before this could be run.
>
> However we intend to change this, meaning this check can be performed for
> F_SEAL_WRITE mappings also.
>
> The logic here is equally applicable to both flags, so update this function
> to accommodate both and rename it accordingly.
>
> Signed-off-by: Lorenzo Stoakes <lstoakes@gmail.com>
For some reason only this one patch landed in my inbox but I've checked all
three on lore and they look good to me. Feel free to add:
Reviewed-by: Jan Kara <jack@suse.cz>
to all of them. Thanks!
Honza
> ---
> fs/hugetlbfs/inode.c | 2 +-
> include/linux/mm.h | 15 ++++++++-------
> mm/shmem.c | 2 +-
> 3 files changed, 10 insertions(+), 9 deletions(-)
>
> diff --git a/fs/hugetlbfs/inode.c b/fs/hugetlbfs/inode.c
> index 06693bb1153d..5c333373dcc9 100644
> --- a/fs/hugetlbfs/inode.c
> +++ b/fs/hugetlbfs/inode.c
> @@ -112,7 +112,7 @@ static int hugetlbfs_file_mmap(struct file *file, struct vm_area_struct *vma)
> vm_flags_set(vma, VM_HUGETLB | VM_DONTEXPAND);
> vma->vm_ops = &hugetlb_vm_ops;
>
> - ret = seal_check_future_write(info->seals, vma);
> + ret = seal_check_write(info->seals, vma);
> if (ret)
> return ret;
>
> diff --git a/include/linux/mm.h b/include/linux/mm.h
> index bae234d18d81..26d7dc3b342b 100644
> --- a/include/linux/mm.h
> +++ b/include/linux/mm.h
> @@ -4078,25 +4078,26 @@ static inline void mem_dump_obj(void *object) {}
> #endif
>
> /**
> - * seal_check_future_write - Check for F_SEAL_FUTURE_WRITE flag and handle it
> + * seal_check_write - Check for F_SEAL_WRITE or F_SEAL_FUTURE_WRITE flags and
> + * handle them.
> * @seals: the seals to check
> * @vma: the vma to operate on
> *
> - * Check whether F_SEAL_FUTURE_WRITE is set; if so, do proper check/handling on
> - * the vma flags. Return 0 if check pass, or <0 for errors.
> + * Check whether F_SEAL_WRITE or F_SEAL_FUTURE_WRITE are set; if so, do proper
> + * check/handling on the vma flags. Return 0 if check pass, or <0 for errors.
> */
> -static inline int seal_check_future_write(int seals, struct vm_area_struct *vma)
> +static inline int seal_check_write(int seals, struct vm_area_struct *vma)
> {
> - if (seals & F_SEAL_FUTURE_WRITE) {
> + if (seals & (F_SEAL_WRITE | F_SEAL_FUTURE_WRITE)) {
> /*
> * New PROT_WRITE and MAP_SHARED mmaps are not allowed when
> - * "future write" seal active.
> + * write seals are active.
> */
> if ((vma->vm_flags & VM_SHARED) && (vma->vm_flags & VM_WRITE))
> return -EPERM;
>
> /*
> - * Since an F_SEAL_FUTURE_WRITE sealed memfd can be mapped as
> + * Since an F_SEAL_[FUTURE_]WRITE sealed memfd can be mapped as
> * MAP_SHARED and read-only, take care to not allow mprotect to
> * revert protections on such mappings. Do this only for shared
> * mappings. For private mappings, don't need to mask
> diff --git a/mm/shmem.c b/mm/shmem.c
> index 6503910b0f54..cab053831fea 100644
> --- a/mm/shmem.c
> +++ b/mm/shmem.c
> @@ -2405,7 +2405,7 @@ static int shmem_mmap(struct file *file, struct vm_area_struct *vma)
> struct shmem_inode_info *info = SHMEM_I(inode);
> int ret;
>
> - ret = seal_check_future_write(info->seals, vma);
> + ret = seal_check_write(info->seals, vma);
> if (ret)
> return ret;
>
> --
> 2.42.0
>
--
Jan Kara <jack@suse.com>
SUSE Labs, CR
^ permalink raw reply [flat|nested] 6+ messages in thread
* [PATCH v4 3/3] mm: perform the mapping_map_writable() check after call_mmap()
2023-10-12 17:04 [PATCH v4 0/3] permit write-sealed memfd read-only shared mappings Lorenzo Stoakes
2023-10-12 17:04 ` [PATCH v4 1/3] mm: drop the assumption that VM_SHARED always implies writable Lorenzo Stoakes
2023-10-12 17:04 ` [PATCH v4 2/3] mm: update memfd seal write check to include F_SEAL_WRITE Lorenzo Stoakes
@ 2023-10-12 17:04 ` Lorenzo Stoakes
2023-10-16 16:27 ` Lorenzo Stoakes
2 siblings, 1 reply; 6+ messages in thread
From: Lorenzo Stoakes @ 2023-10-12 17:04 UTC (permalink / raw)
To: linux-mm, linux-kernel, Andrew Morton
Cc: Mike Kravetz, Muchun Song, Alexander Viro, Christian Brauner,
Matthew Wilcox, Hugh Dickins, Andy Lutomirski, Jan Kara,
linux-fsdevel, bpf, Lorenzo Stoakes
In order for a F_SEAL_WRITE sealed memfd mapping to have an opportunity to
clear VM_MAYWRITE, we must be able to invoke the appropriate vm_ops->mmap()
handler to do so. We would otherwise fail the mapping_map_writable() check
before we had the opportunity to avoid it.
This patch moves this check after the call_mmap() invocation. Only memfd
actively denies write access causing a potential failure here (in
memfd_add_seals()), so there should be no impact on non-memfd cases.
This patch makes the userland-visible change that MAP_SHARED, PROT_READ
mappings of an F_SEAL_WRITE sealed memfd mapping will now succeed.
There is a delicate situation with cleanup paths assuming that a writable
mapping must have occurred in circumstances where it may now not have. In
order to ensure we do not accidentally mark a writable file unwritable by
mistake, we explicitly track whether we have a writable mapping and
unmap only if we do.
Link: https://bugzilla.kernel.org/show_bug.cgi?id=217238
Signed-off-by: Lorenzo Stoakes <lstoakes@gmail.com>
---
mm/mmap.c | 23 ++++++++++++++---------
1 file changed, 14 insertions(+), 9 deletions(-)
diff --git a/mm/mmap.c b/mm/mmap.c
index 0041e3631f6c..7f45a08e7973 100644
--- a/mm/mmap.c
+++ b/mm/mmap.c
@@ -2752,6 +2752,7 @@ unsigned long mmap_region(struct file *file, unsigned long addr,
unsigned long charged = 0;
unsigned long end = addr + len;
unsigned long merge_start = addr, merge_end = end;
+ bool writable_file_mapping = false;
pgoff_t vm_pgoff;
int error;
VMA_ITERATOR(vmi, mm, addr);
@@ -2846,17 +2847,19 @@ unsigned long mmap_region(struct file *file, unsigned long addr,
vma->vm_pgoff = pgoff;
if (file) {
- if (is_shared_maywrite(vm_flags)) {
- error = mapping_map_writable(file->f_mapping);
- if (error)
- goto free_vma;
- }
-
vma->vm_file = get_file(file);
error = call_mmap(file, vma);
if (error)
goto unmap_and_free_vma;
+ if (vma_is_shared_maywrite(vma)) {
+ error = mapping_map_writable(file->f_mapping);
+ if (error)
+ goto close_and_free_vma;
+
+ writable_file_mapping = true;
+ }
+
/*
* Expansion is handled above, merging is handled below.
* Drivers should not alter the address of the VMA.
@@ -2920,8 +2923,10 @@ unsigned long mmap_region(struct file *file, unsigned long addr,
mm->map_count++;
if (vma->vm_file) {
i_mmap_lock_write(vma->vm_file->f_mapping);
- if (vma_is_shared_maywrite(vma))
+ if (vma_is_shared_maywrite(vma)) {
mapping_allow_writable(vma->vm_file->f_mapping);
+ writable_file_mapping = true;
+ }
flush_dcache_mmap_lock(vma->vm_file->f_mapping);
vma_interval_tree_insert(vma, &vma->vm_file->f_mapping->i_mmap);
@@ -2937,7 +2942,7 @@ unsigned long mmap_region(struct file *file, unsigned long addr,
/* Once vma denies write, undo our temporary denial count */
unmap_writable:
- if (file && is_shared_maywrite(vm_flags))
+ if (writable_file_mapping)
mapping_unmap_writable(file->f_mapping);
file = vma->vm_file;
ksm_add_vma(vma);
@@ -2985,7 +2990,7 @@ unsigned long mmap_region(struct file *file, unsigned long addr,
unmap_region(mm, &vmi.mas, vma, prev, next, vma->vm_start,
vma->vm_end, vma->vm_end, true);
}
- if (file && is_shared_maywrite(vm_flags))
+ if (writable_file_mapping)
mapping_unmap_writable(file->f_mapping);
free_vma:
vm_area_free(vma);
--
2.42.0
^ permalink raw reply [flat|nested] 6+ messages in thread* Re: [PATCH v4 3/3] mm: perform the mapping_map_writable() check after call_mmap()
2023-10-12 17:04 ` [PATCH v4 3/3] mm: perform the mapping_map_writable() check after call_mmap() Lorenzo Stoakes
@ 2023-10-16 16:27 ` Lorenzo Stoakes
0 siblings, 0 replies; 6+ messages in thread
From: Lorenzo Stoakes @ 2023-10-16 16:27 UTC (permalink / raw)
To: linux-mm, linux-kernel, Andrew Morton
Cc: Mike Kravetz, Muchun Song, Alexander Viro, Christian Brauner,
Matthew Wilcox, Hugh Dickins, Andy Lutomirski, Jan Kara,
linux-fsdevel, bpf, Naresh Kamboju, lkft-triage
On Thu, Oct 12, 2023 at 06:04:30PM +0100, Lorenzo Stoakes wrote:
> In order for a F_SEAL_WRITE sealed memfd mapping to have an opportunity to
> clear VM_MAYWRITE, we must be able to invoke the appropriate vm_ops->mmap()
> handler to do so. We would otherwise fail the mapping_map_writable() check
> before we had the opportunity to avoid it.
>
> This patch moves this check after the call_mmap() invocation. Only memfd
> actively denies write access causing a potential failure here (in
> memfd_add_seals()), so there should be no impact on non-memfd cases.
>
> This patch makes the userland-visible change that MAP_SHARED, PROT_READ
> mappings of an F_SEAL_WRITE sealed memfd mapping will now succeed.
>
> There is a delicate situation with cleanup paths assuming that a writable
> mapping must have occurred in circumstances where it may now not have. In
> order to ensure we do not accidentally mark a writable file unwritable by
> mistake, we explicitly track whether we have a writable mapping and
> unmap only if we do.
>
> Link: https://bugzilla.kernel.org/show_bug.cgi?id=217238
> Signed-off-by: Lorenzo Stoakes <lstoakes@gmail.com>
> ---
> mm/mmap.c | 23 ++++++++++++++---------
> 1 file changed, 14 insertions(+), 9 deletions(-)
>
[snip]
Andrew, could you apply the following -fix patch to this? As a bug was
detected in the implementation [0] - I was being over-zealous in setting
the writable_file_mapping flag and had falsely assumed vma->vm_file == file
in all instances of the cleanup. The fix is to only set it in one place.
[0]: https://lore.kernel.org/all/CA+G9fYtL7wK-dE-Tnz4t-GWmQb50EPYa=TWGjpgYU2Z=oeAO_w@mail.gmail.com/
----8<----
From 7feea6faada5b10a872c24755cc630220cba619a Mon Sep 17 00:00:00 2001
From: Lorenzo Stoakes <lstoakes@gmail.com>
Date: Mon, 16 Oct 2023 17:17:13 +0100
Subject: [PATCH] mm: perform the mapping_map_writable() check after
call_mmap()
Do not set writable_file_mapping in an instance where it is not appropriate
to do so.
Signed-off-by: Lorenzo Stoakes <lstoakes@gmail.com>
---
mm/mmap.c | 4 +---
1 file changed, 1 insertion(+), 3 deletions(-)
diff --git a/mm/mmap.c b/mm/mmap.c
index 7f45a08e7973..8b57e42fd980 100644
--- a/mm/mmap.c
+++ b/mm/mmap.c
@@ -2923,10 +2923,8 @@ unsigned long mmap_region(struct file *file, unsigned long addr,
mm->map_count++;
if (vma->vm_file) {
i_mmap_lock_write(vma->vm_file->f_mapping);
- if (vma_is_shared_maywrite(vma)) {
+ if (vma_is_shared_maywrite(vma))
mapping_allow_writable(vma->vm_file->f_mapping);
- writable_file_mapping = true;
- }
flush_dcache_mmap_lock(vma->vm_file->f_mapping);
vma_interval_tree_insert(vma, &vma->vm_file->f_mapping->i_mmap);
--
2.42.0
^ permalink raw reply [flat|nested] 6+ messages in thread