1/* $OpenBSD: uvm_unix.c,v 1.73 2024/01/17 22:22:25 kurt Exp $ */ 2/* $NetBSD: uvm_unix.c,v 1.18 2000/09/13 15:00:25 thorpej Exp $ */ 3 4/* 5 * Copyright (c) 1997 Charles D. Cranor and Washington University. 6 * Copyright (c) 1991, 1993 The Regents of the University of California. 7 * Copyright (c) 1988 University of Utah. 8 * 9 * All rights reserved. 10 * 11 * This code is derived from software contributed to Berkeley by 12 * the Systems Programming Group of the University of Utah Computer 13 * Science Department. 14 * 15 * Redistribution and use in source and binary forms, with or without 16 * modification, are permitted provided that the following conditions 17 * are met: 18 * 1. Redistributions of source code must retain the above copyright 19 * notice, this list of conditions and the following disclaimer. 20 * 2. Redistributions in binary form must reproduce the above copyright 21 * notice, this list of conditions and the following disclaimer in the 22 * documentation and/or other materials provided with the distribution. 23 * 3. Neither the name of the University nor the names of its contributors 24 * may be used to endorse or promote products derived from this software 25 * without specific prior written permission. 26 * 27 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 28 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 29 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 30 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 31 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 32 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 33 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 34 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 35 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 36 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 37 * SUCH DAMAGE. 38 * 39 * from: Utah $Hdr: vm_unix.c 1.1 89/11/07$ 40 * @(#)vm_unix.c 8.1 (Berkeley) 6/11/93 41 * from: Id: uvm_unix.c,v 1.1.2.2 1997/08/25 18:52:30 chuck Exp 42 */ 43 44/* 45 * uvm_unix.c: traditional sbrk/grow interface to vm. 46 */ 47 48#include <sys/param.h> 49#include <sys/systm.h> 50#include <sys/proc.h> 51#include <sys/resourcevar.h> 52#include <sys/vnode.h> 53 54#include <sys/mount.h> 55#include <sys/syscallargs.h> 56 57#include <uvm/uvm.h> 58 59/* 60 * sys_obreak: set break 61 */ 62 63int 64sys_obreak(struct proc *p, void *v, register_t *retval) 65{ 66 struct sys_obreak_args /* { 67 syscallarg(char *) nsize; 68 } */ *uap = v; 69 struct vmspace *vm = p->p_vmspace; 70 vaddr_t new, old, base; 71 int error; 72 73 base = (vaddr_t)vm->vm_daddr; 74 new = round_page((vaddr_t)SCARG(uap, nsize)); 75 if (new < base || (new - base) > lim_cur(RLIMIT_DATA)) 76 return (ENOMEM); 77 78 old = round_page(base + ptoa(vm->vm_dsize)); 79 80 if (new == old) 81 return (0); 82 83 /* grow or shrink? */ 84 if (new > old) { 85 error = uvm_map(&vm->vm_map, &old, new - old, NULL, 86 UVM_UNKNOWN_OFFSET, 0, 87 UVM_MAPFLAG(PROT_READ | PROT_WRITE, 88 PROT_READ | PROT_WRITE | PROT_EXEC, MAP_INHERIT_COPY, 89 MADV_NORMAL, UVM_FLAG_FIXED|UVM_FLAG_COPYONW)); 90 if (error) { 91 uprintf("sbrk: grow %ld failed, error = %d\n", 92 new - old, error); 93 return (ENOMEM); 94 } 95 vm->vm_dsize += atop(new - old); 96 } else { 97 uvm_unmap(&vm->vm_map, new, old); 98 vm->vm_dsize -= atop(old - new); 99 } 100 101 return (0); 102} 103 104/* 105 * uvm_grow: enlarge the "stack segment" to include sp. 106 */ 107void 108uvm_grow(struct proc *p, vaddr_t sp) 109{ 110 struct vmspace *vm = p->p_vmspace; 111 vm_map_t map = &vm->vm_map; 112 int si; 113 114 /* For user defined stacks (from sendsig). */ 115 if (sp < (vaddr_t)vm->vm_maxsaddr) 116 return; 117#ifdef MACHINE_STACK_GROWS_UP 118 if (sp >= (vaddr_t)vm->vm_minsaddr) 119 return; 120#endif 121 122 vm_map_lock(map); 123 124 /* For common case of already allocated (from trap). */ 125#ifdef MACHINE_STACK_GROWS_UP 126 if (sp < (vaddr_t)vm->vm_maxsaddr + ptoa(vm->vm_ssize)) 127#else 128 if (sp >= (vaddr_t)vm->vm_minsaddr - ptoa(vm->vm_ssize)) 129#endif 130 goto out; 131 132 /* Really need to check vs limit and increment stack size if ok. */ 133#ifdef MACHINE_STACK_GROWS_UP 134 si = atop(sp - (vaddr_t)vm->vm_maxsaddr) - vm->vm_ssize + 1; 135#else 136 si = atop((vaddr_t)vm->vm_minsaddr - sp) - vm->vm_ssize; 137#endif 138 if (vm->vm_ssize + si <= atop(lim_cur(RLIMIT_STACK))) 139 vm->vm_ssize += si; 140out: 141 vm_map_unlock(map); 142} 143 144#ifndef SMALL_KERNEL 145 146#define WALK_CHUNK 32 147/* 148 * Not all the pages in an amap may be present. When dumping core, 149 * we don't want to force all the pages to be present: it's a waste 150 * of time and memory when we already know what they contain (zeros) 151 * and the ELF format at least can adequately represent them as a 152 * segment with memory size larger than its file size. 153 * 154 * So, we walk the amap with calls to amap_lookups() and scan the 155 * resulting pointers to find ranges of zero or more present pages 156 * followed by at least one absent page or the end of the amap. 157 * When then pass that range to the walk callback with 'start' 158 * pointing to the start of the present range, 'realend' pointing 159 * to the first absent page (or the end of the entry), and 'end' 160 * pointing to the page past the last absent page (or the end of 161 * the entry). 162 * 163 * Note that if the first page of the amap is empty then the callback 164 * must be invoked with 'start' == 'realend' so it can present that 165 * first range of absent pages. 166 */ 167int 168uvm_coredump_walk_amap(struct vm_map_entry *entry, int *nsegmentp, 169 uvm_coredump_walk_cb *walk, void *cookie) 170{ 171 struct vm_anon *anons[WALK_CHUNK]; 172 vaddr_t pos, start, realend, end, entry_end; 173 vm_prot_t prot; 174 int nsegment, absent, npages, i, error; 175 176 prot = entry->protection; 177 nsegment = *nsegmentp; 178 start = entry->start; 179 entry_end = MIN(entry->end, VM_MAXUSER_ADDRESS); 180 181 absent = 0; 182 for (pos = start; pos < entry_end; pos += npages << PAGE_SHIFT) { 183 npages = (entry_end - pos) >> PAGE_SHIFT; 184 if (npages > WALK_CHUNK) 185 npages = WALK_CHUNK; 186 amap_lookups(&entry->aref, pos - entry->start, anons, npages); 187 for (i = 0; i < npages; i++) { 188 if ((anons[i] == NULL) == absent) 189 continue; 190 if (!absent) { 191 /* going from present to absent: set realend */ 192 realend = pos + (i << PAGE_SHIFT); 193 absent = 1; 194 continue; 195 } 196 197 /* going from absent to present: invoke callback */ 198 end = pos + (i << PAGE_SHIFT); 199 if (start != end) { 200 error = (*walk)(start, realend, end, prot, 201 0, nsegment, cookie); 202 if (error) 203 return error; 204 nsegment++; 205 } 206 start = realend = end; 207 absent = 0; 208 } 209 } 210 211 if (!absent) 212 realend = entry_end; 213 error = (*walk)(start, realend, entry_end, prot, 0, nsegment, cookie); 214 *nsegmentp = nsegment + 1; 215 return error; 216} 217 218/* 219 * Common logic for whether a map entry should be included in a coredump 220 */ 221static inline int 222uvm_should_coredump(struct proc *p, struct vm_map_entry *entry) 223{ 224 if (!(entry->protection & PROT_WRITE) && 225 entry->aref.ar_amap == NULL && 226 entry->start != p->p_p->ps_sigcode && 227 entry->start != p->p_p->ps_timekeep) 228 return 0; 229 230 /* 231 * Skip ranges marked as unreadable, as uiomove(UIO_USERSPACE) 232 * will fail on them. Maybe this really should be a test of 233 * entry->max_protection, but doing 234 * uvm_map_extract(UVM_EXTRACT_FIXPROT) 235 * on each such page would suck. 236 */ 237 if (!(entry->protection & PROT_READ) && 238 entry->start != p->p_p->ps_sigcode) 239 return 0; 240 241 /* Skip ranges excluded from coredumps. */ 242 if (UVM_ET_ISCONCEAL(entry)) 243 return 0; 244 245 /* Don't dump mmaped devices. */ 246 if (entry->object.uvm_obj != NULL && 247 UVM_OBJ_IS_DEVICE(entry->object.uvm_obj)) 248 return 0; 249 250 if (entry->start >= VM_MAXUSER_ADDRESS) 251 return 0; 252 253 return 1; 254} 255 256 257/* do nothing callback for uvm_coredump_walk_amap() */ 258static int 259noop(vaddr_t start, vaddr_t realend, vaddr_t end, vm_prot_t prot, 260 int isvnode, int nsegment, void *cookie) 261{ 262 return 0; 263} 264 265/* 266 * Walk the VA space for a process to identify what to write to 267 * a coredump. First the number of contiguous ranges is counted, 268 * then the 'setup' callback is invoked to prepare for actually 269 * recording the ranges, then the VA is walked again, invoking 270 * the 'walk' callback for each range. The number of ranges walked 271 * is guaranteed to match the count seen by the 'setup' callback. 272 */ 273 274int 275uvm_coredump_walkmap(struct proc *p, uvm_coredump_setup_cb *setup, 276 uvm_coredump_walk_cb *walk, void *cookie) 277{ 278 struct vmspace *vm = p->p_vmspace; 279 struct vm_map *map = &vm->vm_map; 280 struct vm_map_entry *entry; 281 vaddr_t end; 282 int refed_amaps = 0; 283 int nsegment, error, isvnode; 284 285 /* 286 * Walk the map once to count the segments. If an amap is 287 * referenced more than once than take *another* reference 288 * and treat the amap as exactly one segment instead of 289 * checking page presence inside it. On the second pass 290 * we'll recognize which amaps we did that for by the ref 291 * count being >1...and decrement it then. 292 */ 293 nsegment = 0; 294 RBT_FOREACH(entry, uvm_map_addr, &map->addr) { 295 /* should never happen for a user process */ 296 if (UVM_ET_ISSUBMAP(entry)) { 297 panic("%s: user process with submap?", __func__); 298 } 299 300 if (! uvm_should_coredump(p, entry)) 301 continue; 302 303 if (entry->aref.ar_amap != NULL) { 304 if (entry->aref.ar_amap->am_ref == 1) { 305 uvm_coredump_walk_amap(entry, &nsegment, 306 &noop, cookie); 307 continue; 308 } 309 310 /* 311 * Multiple refs currently, so take another and 312 * treat it as a single segment 313 */ 314 entry->aref.ar_amap->am_ref++; 315 refed_amaps++; 316 } 317 318 nsegment++; 319 } 320 321 /* 322 * Okay, we have a count in nsegment. Prepare to 323 * walk it again, then invoke the setup callback. 324 */ 325 entry = RBT_MIN(uvm_map_addr, &map->addr); 326 error = (*setup)(nsegment, cookie); 327 if (error) 328 goto cleanup; 329 330 /* 331 * Setup went okay, so do the second walk, invoking the walk 332 * callback on the counted segments and cleaning up references 333 * as we go. 334 */ 335 nsegment = 0; 336 for (; entry != NULL; entry = RBT_NEXT(uvm_map_addr, entry)) { 337 if (! uvm_should_coredump(p, entry)) 338 continue; 339 340 if (entry->aref.ar_amap != NULL && 341 entry->aref.ar_amap->am_ref == 1) { 342 error = uvm_coredump_walk_amap(entry, &nsegment, 343 walk, cookie); 344 if (error) 345 break; 346 continue; 347 } 348 349 end = entry->end; 350 if (end > VM_MAXUSER_ADDRESS) 351 end = VM_MAXUSER_ADDRESS; 352 353 isvnode = (entry->object.uvm_obj != NULL && 354 UVM_OBJ_IS_VNODE(entry->object.uvm_obj)); 355 error = (*walk)(entry->start, end, end, entry->protection, 356 isvnode, nsegment, cookie); 357 if (error) 358 break; 359 nsegment++; 360 361 if (entry->aref.ar_amap != NULL && 362 entry->aref.ar_amap->am_ref > 1) { 363 /* multiple refs, so we need to drop one */ 364 entry->aref.ar_amap->am_ref--; 365 refed_amaps--; 366 } 367 } 368 369 if (error) { 370cleanup: 371 /* clean up the extra references from where we left off */ 372 if (refed_amaps > 0) { 373 for (; entry != NULL; 374 entry = RBT_NEXT(uvm_map_addr, entry)) { 375 if (entry->aref.ar_amap == NULL || 376 entry->aref.ar_amap->am_ref == 1) 377 continue; 378 if (! uvm_should_coredump(p, entry)) 379 continue; 380 entry->aref.ar_amap->am_ref--; 381 if (refed_amaps-- == 0) 382 break; 383 } 384 } 385 } 386 387 return error; 388} 389 390#endif /* !SMALL_KERNEL */ 391