Skip to content

Commit 3dd6ed3

Browse files
lorenzo-stoakesakpm00
authored andcommitted
mm: avoid unsafe VMA hook invocation when error arises on mmap hook
Patch series "fix error handling in mmap_region() and refactor (hotfixes)", v4. mmap_region() is somewhat terrifying, with spaghetti-like control flow and numerous means by which issues can arise and incomplete state, memory leaks and other unpleasantness can occur. A large amount of the complexity arises from trying to handle errors late in the process of mapping a VMA, which forms the basis of recently observed issues with resource leaks and observable inconsistent state. This series goes to great lengths to simplify how mmap_region() works and to avoid unwinding errors late on in the process of setting up the VMA for the new mapping, and equally avoids such operations occurring while the VMA is in an inconsistent state. The patches in this series comprise the minimal changes required to resolve existing issues in mmap_region() error handling, in order that they can be hotfixed and backported. There is additionally a follow up series which goes further, separated out from the v1 series and sent and updated separately. This patch (of 5): After an attempted mmap() fails, we are no longer in a situation where we can safely interact with VMA hooks. This is currently not enforced, meaning that we need complicated handling to ensure we do not incorrectly call these hooks. We can avoid the whole issue by treating the VMA as suspect the moment that the file->f_ops->mmap() function reports an error by replacing whatever VMA operations were installed with a dummy empty set of VMA operations. We do so through a new helper function internal to mm - mmap_file() - which is both more logically named than the existing call_mmap() function and correctly isolates handling of the vm_op reassignment to mm. All the existing invocations of call_mmap() outside of mm are ultimately nested within the call_mmap() from mm, which we now replace. It is therefore safe to leave call_mmap() in place as a convenience function (and to avoid churn). The invokers are: ovl_file_operations -> mmap -> ovl_mmap() -> backing_file_mmap() coda_file_operations -> mmap -> coda_file_mmap() shm_file_operations -> shm_mmap() shm_file_operations_huge -> shm_mmap() dma_buf_fops -> dma_buf_mmap_internal -> i915_dmabuf_ops -> i915_gem_dmabuf_mmap() None of these callers interact with vm_ops or mappings in a problematic way on error, quickly exiting out. Link: https://lkml.kernel.org/r/[email protected] Link: https://lkml.kernel.org/r/d41fd763496fd0048a962f3fd9407dc72dd4fd86.1730224667.git.lorenzo.stoakes@oracle.com Fixes: deb0f65 ("mm/mmap: undo ->mmap() when arch_validate_flags() fails") Signed-off-by: Lorenzo Stoakes <[email protected]> Reported-by: Jann Horn <[email protected]> Reviewed-by: Liam R. Howlett <[email protected]> Reviewed-by: Vlastimil Babka <[email protected]> Reviewed-by: Jann Horn <[email protected]> Cc: Andreas Larsson <[email protected]> Cc: Catalin Marinas <[email protected]> Cc: David S. Miller <[email protected]> Cc: Helge Deller <[email protected]> Cc: James E.J. Bottomley <[email protected]> Cc: Linus Torvalds <[email protected]> Cc: Mark Brown <[email protected]> Cc: Peter Xu <[email protected]> Cc: Will Deacon <[email protected]> Cc: <[email protected]> Signed-off-by: Andrew Morton <[email protected]>
1 parent f8f931b commit 3dd6ed3

File tree

3 files changed

+32
-5
lines changed

3 files changed

+32
-5
lines changed

mm/internal.h

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,33 @@ static inline void *folio_raw_mapping(const struct folio *folio)
108108
return (void *)(mapping & ~PAGE_MAPPING_FLAGS);
109109
}
110110

111+
/*
112+
* This is a file-backed mapping, and is about to be memory mapped - invoke its
113+
* mmap hook and safely handle error conditions. On error, VMA hooks will be
114+
* mutated.
115+
*
116+
* @file: File which backs the mapping.
117+
* @vma: VMA which we are mapping.
118+
*
119+
* Returns: 0 if success, error otherwise.
120+
*/
121+
static inline int mmap_file(struct file *file, struct vm_area_struct *vma)
122+
{
123+
int err = call_mmap(file, vma);
124+
125+
if (likely(!err))
126+
return 0;
127+
128+
/*
129+
* OK, we tried to call the file hook for mmap(), but an error
130+
* arose. The mapping is in an inconsistent state and we most not invoke
131+
* any further hooks on it.
132+
*/
133+
vma->vm_ops = &vma_dummy_vm_ops;
134+
135+
return err;
136+
}
137+
111138
#ifdef CONFIG_MMU
112139

113140
/* Flags for folio_pte_batch(). */

mm/mmap.c

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1422,7 +1422,7 @@ unsigned long mmap_region(struct file *file, unsigned long addr,
14221422
/*
14231423
* clear PTEs while the vma is still in the tree so that rmap
14241424
* cannot race with the freeing later in the truncate scenario.
1425-
* This is also needed for call_mmap(), which is why vm_ops
1425+
* This is also needed for mmap_file(), which is why vm_ops
14261426
* close function is called.
14271427
*/
14281428
vms_clean_up_area(&vms, &mas_detach);
@@ -1447,7 +1447,7 @@ unsigned long mmap_region(struct file *file, unsigned long addr,
14471447

14481448
if (file) {
14491449
vma->vm_file = get_file(file);
1450-
error = call_mmap(file, vma);
1450+
error = mmap_file(file, vma);
14511451
if (error)
14521452
goto unmap_and_free_vma;
14531453

@@ -1470,7 +1470,7 @@ unsigned long mmap_region(struct file *file, unsigned long addr,
14701470

14711471
vma_iter_config(&vmi, addr, end);
14721472
/*
1473-
* If vm_flags changed after call_mmap(), we should try merge
1473+
* If vm_flags changed after mmap_file(), we should try merge
14741474
* vma again as we may succeed this time.
14751475
*/
14761476
if (unlikely(vm_flags != vma->vm_flags && vmg.prev)) {

mm/nommu.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -885,7 +885,7 @@ static int do_mmap_shared_file(struct vm_area_struct *vma)
885885
{
886886
int ret;
887887

888-
ret = call_mmap(vma->vm_file, vma);
888+
ret = mmap_file(vma->vm_file, vma);
889889
if (ret == 0) {
890890
vma->vm_region->vm_top = vma->vm_region->vm_end;
891891
return 0;
@@ -918,7 +918,7 @@ static int do_mmap_private(struct vm_area_struct *vma,
918918
* happy.
919919
*/
920920
if (capabilities & NOMMU_MAP_DIRECT) {
921-
ret = call_mmap(vma->vm_file, vma);
921+
ret = mmap_file(vma->vm_file, vma);
922922
/* shouldn't return success if we're not sharing */
923923
if (WARN_ON_ONCE(!is_nommu_shared_mapping(vma->vm_flags)))
924924
ret = -ENOSYS;

0 commit comments

Comments
 (0)