Skip to content

Commit 77b50f6

Browse files
lorenzo-stoakesgregkh
authored andcommitted
mm: unconditionally close VMAs on error
[ Upstream commit 4080ef1579b2413435413988d14ac8c68e4d42c8 ] Incorrect invocation of VMA callbacks when the VMA is no longer in a consistent state is bug prone and risky to perform. With regards to the important vm_ops->close() callback We have gone to great lengths to try to track whether or not we ought to close VMAs. Rather than doing so and risking making a mistake somewhere, instead unconditionally close and reset vma->vm_ops to an empty dummy operations set with a NULL .close operator. We introduce a new function to do so - vma_close() - and simplify existing vms logic which tracked whether we needed to close or not. This simplifies the logic, avoids incorrect double-calling of the .close() callback and allows us to update error paths to simply call vma_close() unconditionally - making VMA closure idempotent. Link: https://lkml.kernel.org/r/28e89dda96f68c505cb6f8e9fc9b57c3e9f74b42.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: Vlastimil Babka <[email protected]> Reviewed-by: Liam R. Howlett <[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]> Signed-off-by: Lorenzo Stoakes <[email protected]> Signed-off-by: Greg Kroah-Hartman <[email protected]>
1 parent 7443f3e commit 77b50f6

File tree

4 files changed

+27
-10
lines changed

4 files changed

+27
-10
lines changed

mm/internal.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,13 @@ void page_writeback_init(void);
6464
*/
6565
int mmap_file(struct file *file, struct vm_area_struct *vma);
6666

67+
/*
68+
* If the VMA has a close hook then close it, and since closing it might leave
69+
* it in an inconsistent state which makes the use of any hooks suspect, clear
70+
* them down by installing dummy empty hooks.
71+
*/
72+
void vma_close(struct vm_area_struct *vma);
73+
6774
static inline void *folio_raw_mapping(struct folio *folio)
6875
{
6976
unsigned long mapping = (unsigned long)folio->mapping;

mm/mmap.c

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -136,8 +136,7 @@ void unlink_file_vma(struct vm_area_struct *vma)
136136
static void remove_vma(struct vm_area_struct *vma)
137137
{
138138
might_sleep();
139-
if (vma->vm_ops && vma->vm_ops->close)
140-
vma->vm_ops->close(vma);
139+
vma_close(vma);
141140
if (vma->vm_file)
142141
fput(vma->vm_file);
143142
mpol_put(vma_policy(vma));
@@ -2388,8 +2387,7 @@ int __split_vma(struct mm_struct *mm, struct vm_area_struct *vma,
23882387
new->vm_start = new->vm_end;
23892388
new->vm_pgoff = 0;
23902389
/* Clean everything up if vma_adjust failed. */
2391-
if (new->vm_ops && new->vm_ops->close)
2392-
new->vm_ops->close(new);
2390+
vma_close(new);
23932391
if (new->vm_file)
23942392
fput(new->vm_file);
23952393
unlink_anon_vmas(new);
@@ -2885,8 +2883,7 @@ unsigned long mmap_region(struct file *file, unsigned long addr,
28852883
return addr;
28862884

28872885
close_and_free_vma:
2888-
if (vma->vm_ops && vma->vm_ops->close)
2889-
vma->vm_ops->close(vma);
2886+
vma_close(vma);
28902887
unmap_and_free_vma:
28912888
fput(vma->vm_file);
28922889
vma->vm_file = NULL;
@@ -3376,8 +3373,7 @@ struct vm_area_struct *copy_vma(struct vm_area_struct **vmap,
33763373
return new_vma;
33773374

33783375
out_vma_link:
3379-
if (new_vma->vm_ops && new_vma->vm_ops->close)
3380-
new_vma->vm_ops->close(new_vma);
3376+
vma_close(new_vma);
33813377

33823378
if (new_vma->vm_file)
33833379
fput(new_vma->vm_file);

mm/nommu.c

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -650,8 +650,7 @@ static int delete_vma_from_mm(struct vm_area_struct *vma)
650650
*/
651651
static void delete_vma(struct mm_struct *mm, struct vm_area_struct *vma)
652652
{
653-
if (vma->vm_ops && vma->vm_ops->close)
654-
vma->vm_ops->close(vma);
653+
vma_close(vma);
655654
if (vma->vm_file)
656655
fput(vma->vm_file);
657656
put_nommu_region(vma->vm_region);

mm/util.c

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1121,6 +1121,21 @@ int mmap_file(struct file *file, struct vm_area_struct *vma)
11211121
return err;
11221122
}
11231123

1124+
void vma_close(struct vm_area_struct *vma)
1125+
{
1126+
static const struct vm_operations_struct dummy_vm_ops = {};
1127+
1128+
if (vma->vm_ops && vma->vm_ops->close) {
1129+
vma->vm_ops->close(vma);
1130+
1131+
/*
1132+
* The mapping is in an inconsistent state, and no further hooks
1133+
* may be invoked upon it.
1134+
*/
1135+
vma->vm_ops = &dummy_vm_ops;
1136+
}
1137+
}
1138+
11241139
#ifdef CONFIG_PRINTK
11251140
/**
11261141
* mem_dump_obj - Print available provenance information

0 commit comments

Comments
 (0)