1 /* $OpenBSD: uvm_unix.c,v 1.71 2020/10/21 21:24:57 deraadt 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 63 int 64 sys_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 */ 107 void 108 uvm_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; 140 out: 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 */ 167 int 168 uvm_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 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, 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 */ 221 static inline int 222 uvm_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) == 0) 238 return 0; 239 240 /* Skip ranges excluded from coredumps. */ 241 if (UVM_ET_ISCONCEAL(entry)) 242 return 0; 243 244 /* Don't dump mmaped devices. */ 245 if (entry->object.uvm_obj != NULL && 246 UVM_OBJ_IS_DEVICE(entry->object.uvm_obj)) 247 return 0; 248 249 if (entry->start >= VM_MAXUSER_ADDRESS) 250 return 0; 251 252 return 1; 253 } 254 255 256 /* do nothing callback for uvm_coredump_walk_amap() */ 257 static int 258 noop(vaddr_t start, vaddr_t realend, vaddr_t end, vm_prot_t prot, 259 int nsegment, void *cookie) 260 { 261 return 0; 262 } 263 264 /* 265 * Walk the VA space for a process to identify what to write to 266 * a coredump. First the number of contiguous ranges is counted, 267 * then the 'setup' callback is invoked to prepare for actually 268 * recording the ranges, then the VA is walked again, invoking 269 * the 'walk' callback for each range. The number of ranges walked 270 * is guaranteed to match the count seen by the 'setup' callback. 271 */ 272 273 int 274 uvm_coredump_walkmap(struct proc *p, uvm_coredump_setup_cb *setup, 275 uvm_coredump_walk_cb *walk, void *cookie) 276 { 277 struct vmspace *vm = p->p_vmspace; 278 struct vm_map *map = &vm->vm_map; 279 struct vm_map_entry *entry; 280 vaddr_t end; 281 int refed_amaps = 0; 282 int nsegment, error; 283 284 /* 285 * Walk the map once to count the segments. If an amap is 286 * referenced more than once than take *another* reference 287 * and treat the amap as exactly one segment instead of 288 * checking page presence inside it. On the second pass 289 * we'll recognize which amaps we did that for by the ref 290 * count being >1...and decrement it then. 291 */ 292 nsegment = 0; 293 RBT_FOREACH(entry, uvm_map_addr, &map->addr) { 294 /* should never happen for a user process */ 295 if (UVM_ET_ISSUBMAP(entry)) { 296 panic("%s: user process with submap?", __func__); 297 } 298 299 if (! uvm_should_coredump(p, entry)) 300 continue; 301 302 if (entry->aref.ar_amap != NULL) { 303 if (entry->aref.ar_amap->am_ref == 1) { 304 uvm_coredump_walk_amap(entry, &nsegment, 305 &noop, cookie); 306 continue; 307 } 308 309 /* 310 * Multiple refs currently, so take another and 311 * treat it as a single segment 312 */ 313 entry->aref.ar_amap->am_ref++; 314 refed_amaps++; 315 } 316 317 nsegment++; 318 } 319 320 /* 321 * Okay, we have a count in nsegment. Prepare to 322 * walk it again, then invoke the setup callback. 323 */ 324 entry = RBT_MIN(uvm_map_addr, &map->addr); 325 error = (*setup)(nsegment, cookie); 326 if (error) 327 goto cleanup; 328 329 /* 330 * Setup went okay, so do the second walk, invoking the walk 331 * callback on the counted segments and cleaning up references 332 * as we go. 333 */ 334 nsegment = 0; 335 for (; entry != NULL; entry = RBT_NEXT(uvm_map_addr, entry)) { 336 if (! uvm_should_coredump(p, entry)) 337 continue; 338 339 if (entry->aref.ar_amap != NULL && 340 entry->aref.ar_amap->am_ref == 1) { 341 error = uvm_coredump_walk_amap(entry, &nsegment, 342 walk, cookie); 343 if (error) 344 break; 345 continue; 346 } 347 348 end = entry->end; 349 if (end > VM_MAXUSER_ADDRESS) 350 end = VM_MAXUSER_ADDRESS; 351 352 error = (*walk)(entry->start, end, end, entry->protection, 353 nsegment, cookie); 354 if (error) 355 break; 356 nsegment++; 357 358 if (entry->aref.ar_amap != NULL && 359 entry->aref.ar_amap->am_ref > 1) { 360 /* multiple refs, so we need to drop one */ 361 entry->aref.ar_amap->am_ref--; 362 refed_amaps--; 363 } 364 } 365 366 if (error) { 367 cleanup: 368 /* clean up the extra references from where we left off */ 369 if (refed_amaps > 0) { 370 for (; entry != NULL; 371 entry = RBT_NEXT(uvm_map_addr, entry)) { 372 if (entry->aref.ar_amap == NULL || 373 entry->aref.ar_amap->am_ref == 1) 374 continue; 375 if (! uvm_should_coredump(p, entry)) 376 continue; 377 entry->aref.ar_amap->am_ref--; 378 if (refed_amaps-- == 0) 379 break; 380 } 381 } 382 } 383 384 return error; 385 } 386 387 #endif /* !SMALL_KERNEL */ 388