From 5f69141a284a641310e0943fa09c7abc6a059175 Mon Sep 17 00:00:00 2001 From: kettenis Date: Fri, 11 Mar 2022 19:24:19 +0000 Subject: [PATCH] Hold a read lock on the map while copying out data during a sysctl(2) call to prevent another thread from unmapping the memory and triggering an assertion or even corrupting random physical memory pages. This fix is similar to the change in uvm_glue.c rev. 1.74. However in this case we need to be careful since some sysctl(2) calls look at the map of the current process. In those cases we must not attempt to lock the map again. ok mpi@ Should fix: Reported-by: syzbot+be89fe83d6c004fcb412@syzkaller.appspotmail.com --- sys/uvm/uvm_extern.h | 3 ++- sys/uvm/uvm_glue.c | 20 +++++++++++++++++--- sys/uvm/uvm_io.c | 4 +++- sys/uvm/uvm_map.c | 20 +++++++++++++++----- sys/uvm/uvm_map.h | 5 +++-- 5 files changed, 40 insertions(+), 12 deletions(-) diff --git a/sys/uvm/uvm_extern.h b/sys/uvm/uvm_extern.h index faa4a2e5449..980c97422ef 100644 --- a/sys/uvm/uvm_extern.h +++ b/sys/uvm/uvm_extern.h @@ -1,4 +1,4 @@ -/* $OpenBSD: uvm_extern.h,v 1.158 2021/06/28 11:19:01 mpi Exp $ */ +/* $OpenBSD: uvm_extern.h,v 1.159 2022/03/11 19:24:19 kettenis Exp $ */ /* $NetBSD: uvm_extern.h,v 1.57 2001/03/09 01:02:12 chs Exp $ */ /* @@ -291,6 +291,7 @@ void uvm_init_percpu(void); int uvm_io(vm_map_t, struct uio *, int); #define UVM_IO_FIXPROT 0x01 +#define UVM_IO_RDLOCKED 0x02 vaddr_t uvm_km_alloc1(vm_map_t, vsize_t, vsize_t, boolean_t); void uvm_km_free(vm_map_t, vaddr_t, vsize_t); diff --git a/sys/uvm/uvm_glue.c b/sys/uvm/uvm_glue.c index 206b84e88e2..d8ca78859ea 100644 --- a/sys/uvm/uvm_glue.c +++ b/sys/uvm/uvm_glue.c @@ -1,4 +1,4 @@ -/* $OpenBSD: uvm_glue.c,v 1.81 2022/02/18 09:04:38 kettenis Exp $ */ +/* $OpenBSD: uvm_glue.c,v 1.82 2022/03/11 19:24:19 kettenis Exp $ */ /* $NetBSD: uvm_glue.c,v 1.44 2001/02/06 19:54:44 eeh Exp $ */ /* @@ -110,13 +110,26 @@ uvm_vslock(struct proc *p, caddr_t addr, size_t len, vm_prot_t access_type) { struct vm_map *map = &p->p_vmspace->vm_map; vaddr_t start, end; + int error, mapv; start = trunc_page((vaddr_t)addr); end = round_page((vaddr_t)addr + len); if (end <= start) return (EINVAL); - return uvm_fault_wire(map, start, end, access_type); + vm_map_lock_read(map); +retry: + mapv = map->timestamp; + vm_map_unlock_read(map); + + if ((error = uvm_fault_wire(map, start, end, access_type))) + return (error); + + vm_map_lock_read(map); + if (mapv != map->timestamp) + goto retry; + + return (0); } /* @@ -133,7 +146,8 @@ uvm_vsunlock(struct proc *p, caddr_t addr, size_t len) end = round_page((vaddr_t)addr + len); KASSERT(end > start); - uvm_fault_unwire(&p->p_vmspace->vm_map, start, end); + uvm_fault_unwire_locked(&p->p_vmspace->vm_map, start, end); + vm_map_unlock_read(&p->p_vmspace->vm_map); } /* diff --git a/sys/uvm/uvm_io.c b/sys/uvm/uvm_io.c index 96a9ba543b1..e18df842fe1 100644 --- a/sys/uvm/uvm_io.c +++ b/sys/uvm/uvm_io.c @@ -1,4 +1,4 @@ -/* $OpenBSD: uvm_io.c,v 1.27 2021/03/20 10:24:21 mpi Exp $ */ +/* $OpenBSD: uvm_io.c,v 1.28 2022/03/11 19:24:19 kettenis Exp $ */ /* $NetBSD: uvm_io.c,v 1.12 2000/06/27 17:29:23 mrg Exp $ */ /* @@ -86,6 +86,8 @@ uvm_io(vm_map_t map, struct uio *uio, int flags) extractflags = 0; if (flags & UVM_IO_FIXPROT) extractflags |= UVM_EXTRACT_FIXPROT; + if (flags & UVM_IO_RDLOCKED) + extractflags |= UVM_EXTRACT_RDLOCKED; /* * step 1: main loop... while we've got data to move diff --git a/sys/uvm/uvm_map.c b/sys/uvm/uvm_map.c index ab114029180..98aa7d4326e 100644 --- a/sys/uvm/uvm_map.c +++ b/sys/uvm/uvm_map.c @@ -1,4 +1,4 @@ -/* $OpenBSD: uvm_map.c,v 1.288 2022/02/15 11:54:19 kn Exp $ */ +/* $OpenBSD: uvm_map.c,v 1.289 2022/03/11 19:24:19 kettenis Exp $ */ /* $NetBSD: uvm_map.c,v 1.86 2000/11/27 08:40:03 chs Exp $ */ /* @@ -4522,7 +4522,12 @@ uvm_map_extract(struct vm_map *srcmap, vaddr_t start, vsize_t len, return 0; /* Acquire lock on srcmap. */ - vm_map_lock(srcmap); + if (flags & UVM_EXTRACT_RDLOCKED) { + vm_map_busy(srcmap); + vm_map_upgrade(srcmap); + vm_map_unbusy(srcmap); + } else + vm_map_lock(srcmap); /* Lock srcmap, lookup first and last entry in . */ first = uvm_map_entrybyaddr(&srcmap->addr, start); @@ -4624,7 +4629,10 @@ fail2: vm_map_unlock(kernel_map); fail: - vm_map_unlock(srcmap); + if (flags & UVM_EXTRACT_RDLOCKED) + vm_map_downgrade(srcmap); + else + vm_map_unlock(srcmap); uvm_unmap_detach(&dead, 0); @@ -5581,7 +5589,9 @@ uvm_map_fill_vmmap(struct vm_map *map, struct kinfo_vmentry *kve, */ start = (vaddr_t)kve[0].kve_start; - vm_map_lock(map); + vm_map_busy(map); + vm_map_upgrade(map); + vm_map_unbusy(map); RBT_FOREACH(entry, uvm_map_addr, &map->addr) { if (cnt == maxcnt) { error = ENOMEM; @@ -5605,7 +5615,7 @@ uvm_map_fill_vmmap(struct vm_map *map, struct kinfo_vmentry *kve, kve++; cnt++; } - vm_map_unlock(map); + vm_map_downgrade(map); KASSERT(cnt <= maxcnt); diff --git a/sys/uvm/uvm_map.h b/sys/uvm/uvm_map.h index 24bf9fd7ad1..8d0d1c289f7 100644 --- a/sys/uvm/uvm_map.h +++ b/sys/uvm/uvm_map.h @@ -1,4 +1,4 @@ -/* $OpenBSD: uvm_map.h,v 1.73 2022/02/11 09:25:04 kn Exp $ */ +/* $OpenBSD: uvm_map.h,v 1.74 2022/03/11 19:24:19 kettenis Exp $ */ /* $NetBSD: uvm_map.h,v 1.24 2001/02/18 21:19:08 chs Exp $ */ /* @@ -116,7 +116,8 @@ /* * extract flags */ -#define UVM_EXTRACT_FIXPROT 0x8 /* set prot to maxprot as we go */ +#define UVM_EXTRACT_FIXPROT 0x1 /* set prot to maxprot as we go */ +#define UVM_EXTRACT_RDLOCKED 0x2 /* map is already read-locked */ #endif /* _KERNEL */ -- 2.20.1