aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMark Johnston <markj@FreeBSD.org>2023-02-13 21:24:40 +0000
committerMark Johnston <markj@FreeBSD.org>2023-02-27 13:22:33 +0000
commit2f57ef2d3b8f776a28e195cd780a3bb4924570be (patch)
tree638e6966fb486f085ab24bdb3d62a32a8b91ccaf
parent7af6e00d62f3ba75c91146015724b44a05567efe (diff)
downloadsrc-2f57ef2d3b8f776a28e195cd780a3bb4924570be.tar.gz
src-2f57ef2d3b8f776a28e195cd780a3bb4924570be.zip
vm_fault: Fix a race in vm_fault_soft_fast()
When vm_fault_soft_fast() creates a mapping, it release the VM map lock before unbusying the top-level object. Without the map lock, however, nothing prevents the VM object from being deallocated while still busy. Fix the problem by unbusying the object before releasing the VM map lock. If vm_fault_soft_fast() fails to create a mapping, the VM map lock is not released, so those cases don't need to change. Reported by: syzkaller Reviewed by: kib (previous version) Sponsored by: The FreeBSD Foundation MFC after: 2 weeks Differential Revision: https://reviews.freebsd.org/D38527 (cherry picked from commit d0991948182a1a149ee84f1b9c4d3e30450c8f0b)
-rw-r--r--sys/vm/vm_fault.c21
1 files changed, 8 insertions, 13 deletions
diff --git a/sys/vm/vm_fault.c b/sys/vm/vm_fault.c
index 605cf1203554..4872990c33ec 100644
--- a/sys/vm/vm_fault.c
+++ b/sys/vm/vm_fault.c
@@ -322,20 +322,16 @@ vm_fault_soft_fast(struct faultstate *fs)
#endif
int psind;
vm_offset_t vaddr;
- enum fault_status res;
MPASS(fs->vp == NULL);
- res = FAULT_SUCCESS;
vaddr = fs->vaddr;
vm_object_busy(fs->first_object);
m = vm_page_lookup(fs->first_object, fs->first_pindex);
/* A busy page can be mapped for read|execute access. */
if (m == NULL || ((fs->prot & VM_PROT_WRITE) != 0 &&
- vm_page_busied(m)) || !vm_page_all_valid(m)) {
- res = FAULT_FAILURE;
- goto out;
- }
+ vm_page_busied(m)) || !vm_page_all_valid(m))
+ goto fail;
m_map = m;
psind = 0;
#if VM_NRESERVLEVEL > 0
@@ -370,10 +366,8 @@ vm_fault_soft_fast(struct faultstate *fs)
#endif
if (pmap_enter(fs->map->pmap, vaddr, m_map, fs->prot, fs->fault_type |
PMAP_ENTER_NOSLEEP | (fs->wired ? PMAP_ENTER_WIRED : 0), psind) !=
- KERN_SUCCESS) {
- res = FAULT_FAILURE;
- goto out;
- }
+ KERN_SUCCESS)
+ goto fail;
if (fs->m_hold != NULL) {
(*fs->m_hold) = m;
vm_page_wire(m);
@@ -382,12 +376,13 @@ vm_fault_soft_fast(struct faultstate *fs)
vm_fault_prefault(fs, vaddr, PFBAK, PFFOR, true);
VM_OBJECT_RUNLOCK(fs->first_object);
vm_fault_dirty(fs, m);
+ vm_object_unbusy(fs->first_object);
vm_map_lookup_done(fs->map, fs->entry);
curthread->td_ru.ru_minflt++;
-
-out:
+ return (FAULT_SUCCESS);
+fail:
vm_object_unbusy(fs->first_object);
- return (res);
+ return (FAULT_FAILURE);
}
static void