Skip to content

Commit 4080ef1

Browse files
lorenzo-stoakesakpm00
authored andcommitted
mm: unconditionally close VMAs on error
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]>
1 parent 3dd6ed3 commit 4080ef1

File tree

5 files changed

+27
-17
lines changed

5 files changed

+27
-17
lines changed

mm/internal.h

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,24 @@ static inline int mmap_file(struct file *file, struct vm_area_struct *vma)
135135
return err;
136136
}
137137

138+
/*
139+
* If the VMA has a close hook then close it, and since closing it might leave
140+
* it in an inconsistent state which makes the use of any hooks suspect, clear
141+
* them down by installing dummy empty hooks.
142+
*/
143+
static inline void vma_close(struct vm_area_struct *vma)
144+
{
145+
if (vma->vm_ops && vma->vm_ops->close) {
146+
vma->vm_ops->close(vma);
147+
148+
/*
149+
* The mapping is in an inconsistent state, and no further hooks
150+
* may be invoked upon it.
151+
*/
152+
vma->vm_ops = &vma_dummy_vm_ops;
153+
}
154+
}
155+
138156
#ifdef CONFIG_MMU
139157

140158
/* Flags for folio_pte_batch(). */

mm/mmap.c

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1573,8 +1573,7 @@ unsigned long mmap_region(struct file *file, unsigned long addr,
15731573
return addr;
15741574

15751575
close_and_free_vma:
1576-
if (file && !vms.closed_vm_ops && vma->vm_ops && vma->vm_ops->close)
1577-
vma->vm_ops->close(vma);
1576+
vma_close(vma);
15781577

15791578
if (file || vma->vm_file) {
15801579
unmap_and_free_vma:
@@ -1934,7 +1933,7 @@ void exit_mmap(struct mm_struct *mm)
19341933
do {
19351934
if (vma->vm_flags & VM_ACCOUNT)
19361935
nr_accounted += vma_pages(vma);
1937-
remove_vma(vma, /* unreachable = */ true, /* closed = */ false);
1936+
remove_vma(vma, /* unreachable = */ true);
19381937
count++;
19391938
cond_resched();
19401939
vma = vma_next(&vmi);

mm/nommu.c

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -589,8 +589,7 @@ static int delete_vma_from_mm(struct vm_area_struct *vma)
589589
*/
590590
static void delete_vma(struct mm_struct *mm, struct vm_area_struct *vma)
591591
{
592-
if (vma->vm_ops && vma->vm_ops->close)
593-
vma->vm_ops->close(vma);
592+
vma_close(vma);
594593
if (vma->vm_file)
595594
fput(vma->vm_file);
596595
put_nommu_region(vma->vm_region);

mm/vma.c

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -323,11 +323,10 @@ static bool can_vma_merge_right(struct vma_merge_struct *vmg,
323323
/*
324324
* Close a vm structure and free it.
325325
*/
326-
void remove_vma(struct vm_area_struct *vma, bool unreachable, bool closed)
326+
void remove_vma(struct vm_area_struct *vma, bool unreachable)
327327
{
328328
might_sleep();
329-
if (!closed && vma->vm_ops && vma->vm_ops->close)
330-
vma->vm_ops->close(vma);
329+
vma_close(vma);
331330
if (vma->vm_file)
332331
fput(vma->vm_file);
333332
mpol_put(vma_policy(vma));
@@ -1115,9 +1114,7 @@ void vms_clean_up_area(struct vma_munmap_struct *vms,
11151114
vms_clear_ptes(vms, mas_detach, true);
11161115
mas_set(mas_detach, 0);
11171116
mas_for_each(mas_detach, vma, ULONG_MAX)
1118-
if (vma->vm_ops && vma->vm_ops->close)
1119-
vma->vm_ops->close(vma);
1120-
vms->closed_vm_ops = true;
1117+
vma_close(vma);
11211118
}
11221119

11231120
/*
@@ -1160,7 +1157,7 @@ void vms_complete_munmap_vmas(struct vma_munmap_struct *vms,
11601157
/* Remove and clean up vmas */
11611158
mas_set(mas_detach, 0);
11621159
mas_for_each(mas_detach, vma, ULONG_MAX)
1163-
remove_vma(vma, /* = */ false, vms->closed_vm_ops);
1160+
remove_vma(vma, /* unreachable = */ false);
11641161

11651162
vm_unacct_memory(vms->nr_accounted);
11661163
validate_mm(mm);
@@ -1684,8 +1681,7 @@ struct vm_area_struct *copy_vma(struct vm_area_struct **vmap,
16841681
return new_vma;
16851682

16861683
out_vma_link:
1687-
if (new_vma->vm_ops && new_vma->vm_ops->close)
1688-
new_vma->vm_ops->close(new_vma);
1684+
vma_close(new_vma);
16891685

16901686
if (new_vma->vm_file)
16911687
fput(new_vma->vm_file);

mm/vma.h

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,6 @@ struct vma_munmap_struct {
4242
int vma_count; /* Number of vmas that will be removed */
4343
bool unlock; /* Unlock after the munmap */
4444
bool clear_ptes; /* If there are outstanding PTE to be cleared */
45-
bool closed_vm_ops; /* call_mmap() was encountered, so vmas may be closed */
4645
/* 1 byte hole */
4746
unsigned long nr_pages; /* Number of pages being removed */
4847
unsigned long locked_vm; /* Number of locked pages */
@@ -198,7 +197,6 @@ static inline void init_vma_munmap(struct vma_munmap_struct *vms,
198197
vms->unmap_start = FIRST_USER_ADDRESS;
199198
vms->unmap_end = USER_PGTABLES_CEILING;
200199
vms->clear_ptes = false;
201-
vms->closed_vm_ops = false;
202200
}
203201
#endif
204202

@@ -269,7 +267,7 @@ int do_vmi_munmap(struct vma_iterator *vmi, struct mm_struct *mm,
269267
unsigned long start, size_t len, struct list_head *uf,
270268
bool unlock);
271269

272-
void remove_vma(struct vm_area_struct *vma, bool unreachable, bool closed);
270+
void remove_vma(struct vm_area_struct *vma, bool unreachable);
273271

274272
void unmap_region(struct ma_state *mas, struct vm_area_struct *vma,
275273
struct vm_area_struct *prev, struct vm_area_struct *next);

0 commit comments

Comments
 (0)