LCOV - code coverage report
Current view: top level - uvm - uvm_unix.c (source / functions) Hit Total Coverage
Test: 6.4 Lines: 0 125 0.0 %
Date: 2018-10-19 03:25:38 Functions: 0 6 0.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*      $OpenBSD: uvm_unix.c,v 1.64 2017/03/09 20:27:41 guenther 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           0 : sys_obreak(struct proc *p, void *v, register_t *retval)
      65             : {
      66             :         struct sys_obreak_args /* {
      67             :                 syscallarg(char *) nsize;
      68           0 :         } */ *uap = v;
      69           0 :         struct vmspace *vm = p->p_vmspace;
      70           0 :         vaddr_t new, old, base;
      71             :         int error;
      72             : 
      73           0 :         base = (vaddr_t)vm->vm_daddr;
      74           0 :         new = round_page((vaddr_t)SCARG(uap, nsize));
      75           0 :         if (new < base || (new - base) > p->p_rlimit[RLIMIT_DATA].rlim_cur)
      76           0 :                 return (ENOMEM);
      77             : 
      78           0 :         old = round_page(base + ptoa(vm->vm_dsize));
      79             : 
      80           0 :         if (new == old)
      81           0 :                 return (0);
      82             : 
      83             :         /* grow or shrink? */
      84           0 :         if (new > old) {
      85           0 :                 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           0 :                 if (error) {
      91           0 :                         uprintf("sbrk: grow %ld failed, error = %d\n",
      92             :                             new - old, error);
      93           0 :                         return (ENOMEM);
      94             :                 }
      95           0 :                 vm->vm_dsize += atop(new - old);
      96           0 :         } else {
      97           0 :                 uvm_deallocate(&vm->vm_map, new, old - new);
      98           0 :                 vm->vm_dsize -= atop(old - new);
      99             :         }
     100             : 
     101           0 :         return (0);
     102           0 : }
     103             : 
     104             : /*
     105             :  * uvm_grow: enlarge the "stack segment" to include sp.
     106             :  */
     107             : void
     108           0 : uvm_grow(struct proc *p, vaddr_t sp)
     109             : {
     110           0 :         struct vmspace *vm = p->p_vmspace;
     111             :         int si;
     112             : 
     113             :         /* For user defined stacks (from sendsig). */
     114           0 :         if (sp < (vaddr_t)vm->vm_maxsaddr)
     115           0 :                 return;
     116             : 
     117             :         /* For common case of already allocated (from trap). */
     118             : #ifdef MACHINE_STACK_GROWS_UP
     119             :         if (sp < (vaddr_t)vm->vm_maxsaddr + ptoa(vm->vm_ssize))
     120             : #else
     121           0 :         if (sp >= (vaddr_t)vm->vm_minsaddr - ptoa(vm->vm_ssize))
     122             : #endif
     123           0 :                 return;
     124             : 
     125             :         /* Really need to check vs limit and increment stack size if ok. */
     126             : #ifdef MACHINE_STACK_GROWS_UP
     127             :         si = atop(sp - (vaddr_t)vm->vm_maxsaddr) - vm->vm_ssize + 1;
     128             : #else
     129           0 :         si = atop((vaddr_t)vm->vm_minsaddr - sp) - vm->vm_ssize;
     130             : #endif
     131           0 :         if (vm->vm_ssize + si <= atop(p->p_rlimit[RLIMIT_STACK].rlim_cur))
     132           0 :                 vm->vm_ssize += si;
     133           0 : }
     134             : 
     135             : #ifndef SMALL_KERNEL
     136             : 
     137             : #define WALK_CHUNK      32
     138             : /*
     139             :  * Not all the pages in an amap may be present.  When dumping core,
     140             :  * we don't want to force all the pages to be present: it's a waste
     141             :  * of time and memory when we already know what they contain (zeros)
     142             :  * and the ELF format at least can adequately represent them as a
     143             :  * segment with memory size larger than its file size.
     144             :  *
     145             :  * So, we walk the amap with calls to amap_lookups() and scan the
     146             :  * resulting pointers to find ranges of zero or more present pages
     147             :  * followed by at least one absent page or the end of the amap.
     148             :  * When then pass that range to the walk callback with 'start'
     149             :  * pointing to the start of the present range, 'realend' pointing
     150             :  * to the first absent page (or the end of the entry), and 'end'
     151             :  * pointing to the page page the last absent page (or the end of
     152             :  * the entry).
     153             :  *
     154             :  * Note that if the first page of the amap is empty then the callback
     155             :  * must be invoked with 'start' == 'realend' so it can present that
     156             :  * first range of absent pages.
     157             :  */
     158             : int
     159           0 : uvm_coredump_walk_amap(struct vm_map_entry *entry, int *nsegmentp,
     160             :     uvm_coredump_walk_cb *walk, void *cookie)
     161             : {
     162           0 :         struct vm_anon *anons[WALK_CHUNK];
     163             :         vaddr_t pos, start, realend, end, entry_end;
     164             :         vm_prot_t prot;
     165             :         int nsegment, absent, npages, i, error;
     166             : 
     167           0 :         prot = entry->protection;
     168           0 :         nsegment = *nsegmentp;
     169           0 :         start = entry->start;
     170           0 :         entry_end = MIN(entry->end, VM_MAXUSER_ADDRESS);
     171             : 
     172             :         absent = 0;
     173           0 :         for (pos = start; pos < entry_end; pos += npages << PAGE_SHIFT) {
     174           0 :                 npages = (entry_end - pos) >> PAGE_SHIFT;
     175           0 :                 if (npages > WALK_CHUNK)
     176             :                         npages = WALK_CHUNK;
     177           0 :                 amap_lookups(&entry->aref, pos - entry->start, anons, npages);
     178           0 :                 for (i = 0; i < npages; i++) {
     179           0 :                         if ((anons[i] == NULL) == absent)
     180             :                                 continue;
     181           0 :                         if (!absent) {
     182             :                                 /* going from present to absent: set realend */
     183             :                                 realend = pos + (i << PAGE_SHIFT);
     184             :                                 absent = 1;
     185           0 :                                 continue;
     186             :                         }
     187             : 
     188             :                         /* going from absent to present: invoke callback */
     189             :                         end = pos + (i << PAGE_SHIFT);
     190           0 :                         if (start != end) {
     191           0 :                                 error = (*walk)(start, realend, end, prot,
     192             :                                     nsegment, cookie);
     193           0 :                                 if (error)
     194           0 :                                         return error;
     195           0 :                                 nsegment++;
     196           0 :                         }
     197             :                         start = realend = end;
     198             :                         absent = 0;
     199           0 :                 }
     200             :         }
     201             : 
     202           0 :         if (!absent)
     203           0 :                 realend = entry_end;
     204           0 :         error = (*walk)(start, realend, entry_end, prot, nsegment, cookie);
     205           0 :         *nsegmentp = nsegment + 1;
     206           0 :         return error;
     207           0 : }
     208             : 
     209             : /*
     210             :  * Common logic for whether a map entry should be included in a coredump
     211             :  */
     212             : static inline int
     213           0 : uvm_should_coredump(struct proc *p, struct vm_map_entry *entry)
     214             : {
     215           0 :         if (!(entry->protection & PROT_WRITE) &&
     216           0 :             entry->aref.ar_amap == NULL &&
     217           0 :             entry->start != p->p_p->ps_sigcode)
     218           0 :                 return 0;
     219             : 
     220             :         /*
     221             :          * Skip ranges marked as unreadable, as uiomove(UIO_USERSPACE)
     222             :          * will fail on them.  Maybe this really should be a test of
     223             :          * entry->max_protection, but doing
     224             :          *      uvm_map_extract(UVM_EXTRACT_FIXPROT)
     225             :          * on each such page would suck.
     226             :          */
     227           0 :         if ((entry->protection & PROT_READ) == 0)
     228           0 :                 return 0;
     229             : 
     230             :         /* Don't dump mmaped devices. */
     231           0 :         if (entry->object.uvm_obj != NULL &&
     232           0 :             UVM_OBJ_IS_DEVICE(entry->object.uvm_obj))
     233           0 :                 return 0;
     234             : 
     235           0 :         if (entry->start >= VM_MAXUSER_ADDRESS)
     236           0 :                 return 0;
     237             : 
     238           0 :         return 1;
     239           0 : }
     240             : 
     241             : 
     242             : /* do nothing callback for uvm_coredump_walk_amap() */
     243             : static int
     244           0 : noop(vaddr_t start, vaddr_t realend, vaddr_t end, vm_prot_t prot,
     245             :     int nsegment, void *cookie)
     246             : {
     247           0 :         return 0;
     248             : }
     249             : 
     250             : /*
     251             :  * Walk the VA space for a process to identify what to write to
     252             :  * a coredump.  First the number of contiguous ranges is counted,
     253             :  * then the 'setup' callback is invoked to prepare for actually
     254             :  * recording the ranges, then the VA is walked again, invoking
     255             :  * the 'walk' callback for each range.  The number of ranges walked
     256             :  * is guaranteed to match the count seen by the 'setup' callback.
     257             :  */
     258             : 
     259             : int
     260           0 : uvm_coredump_walkmap(struct proc *p, uvm_coredump_setup_cb *setup,
     261             :     uvm_coredump_walk_cb *walk, void *cookie)
     262             : {
     263           0 :         struct vmspace *vm = p->p_vmspace;
     264           0 :         struct vm_map *map = &vm->vm_map;
     265             :         struct vm_map_entry *entry;
     266             :         vaddr_t end;
     267             :         int refed_amaps = 0;
     268           0 :         int nsegment, error;
     269             : 
     270             :         /*
     271             :          * Walk the map once to count the segments.  If an amap is
     272             :          * referenced more than once than take *another* reference
     273             :          * and treat the amap as exactly one segment instead of
     274             :          * checking page presence inside it.  On the second pass
     275             :          * we'll recognize which amaps we did that for by the ref
     276             :          * count being >1...and decrement it then.
     277             :          */
     278           0 :         nsegment = 0;
     279           0 :         RBT_FOREACH(entry, uvm_map_addr, &map->addr) {
     280             :                 /* should never happen for a user process */
     281           0 :                 if (UVM_ET_ISSUBMAP(entry)) {
     282           0 :                         panic("%s: user process with submap?", __func__);
     283             :                 }
     284             : 
     285           0 :                 if (! uvm_should_coredump(p, entry))
     286             :                         continue;
     287             : 
     288           0 :                 if (entry->aref.ar_amap != NULL) {
     289           0 :                         if (entry->aref.ar_amap->am_ref == 1) {
     290           0 :                                 uvm_coredump_walk_amap(entry, &nsegment,
     291             :                                     &noop, cookie);
     292           0 :                                 continue;
     293             :                         }
     294             : 
     295             :                         /*
     296             :                          * Multiple refs currently, so take another and
     297             :                          * treat it as a single segment
     298             :                          */
     299           0 :                         entry->aref.ar_amap->am_ref++;
     300           0 :                         refed_amaps++;
     301           0 :                 }
     302             : 
     303           0 :                 nsegment++;
     304           0 :         }
     305             : 
     306             :         /*
     307             :          * Okay, we have a count in nsegment.  Prepare to
     308             :          * walk it again, then invoke the setup callback. 
     309             :          */
     310           0 :         entry = RBT_MIN(uvm_map_addr, &map->addr);
     311           0 :         error = (*setup)(nsegment, cookie);
     312           0 :         if (error)
     313             :                 goto cleanup;
     314             : 
     315             :         /*
     316             :          * Setup went okay, so do the second walk, invoking the walk
     317             :          * callback on the counted segments and cleaning up references
     318             :          * as we go.
     319             :          */
     320           0 :         nsegment = 0;
     321           0 :         for (; entry != NULL; entry = RBT_NEXT(uvm_map_addr, entry)) {
     322           0 :                 if (! uvm_should_coredump(p, entry))
     323             :                         continue;
     324             : 
     325           0 :                 if (entry->aref.ar_amap != NULL &&
     326           0 :                     entry->aref.ar_amap->am_ref == 1) {
     327           0 :                         error = uvm_coredump_walk_amap(entry, &nsegment,
     328             :                             walk, cookie);
     329           0 :                         if (error)
     330             :                                 break;
     331             :                         continue;
     332             :                 }
     333             : 
     334           0 :                 end = entry->end;
     335           0 :                 if (end > VM_MAXUSER_ADDRESS)
     336             :                         end = VM_MAXUSER_ADDRESS;
     337             : 
     338           0 :                 error = (*walk)(entry->start, end, end, entry->protection,
     339           0 :                     nsegment, cookie);
     340           0 :                 if (error)
     341             :                         break;
     342           0 :                 nsegment++;
     343             : 
     344           0 :                 if (entry->aref.ar_amap != NULL &&
     345           0 :                     entry->aref.ar_amap->am_ref > 1) {
     346             :                         /* multiple refs, so we need to drop one */
     347           0 :                         entry->aref.ar_amap->am_ref--;
     348           0 :                         refed_amaps--;
     349           0 :                 }
     350             :         }
     351             : 
     352           0 :         if (error) {
     353             : cleanup:
     354             :                 /* clean up the extra references from where we left off */
     355           0 :                 if (refed_amaps > 0) {
     356           0 :                         for (; entry != NULL;
     357           0 :                             entry = RBT_NEXT(uvm_map_addr, entry)) {
     358           0 :                                 if (entry->aref.ar_amap == NULL ||
     359           0 :                                     entry->aref.ar_amap->am_ref == 1)
     360             :                                         continue;
     361           0 :                                 if (! uvm_should_coredump(p, entry))
     362             :                                         continue;
     363           0 :                                 entry->aref.ar_amap->am_ref--;
     364           0 :                                 if (refed_amaps-- == 0)
     365             :                                         break;
     366             :                         }
     367             :                 }
     368             :         }
     369             : 
     370           0 :         return error;
     371           0 : }
     372             : 
     373             : #endif  /* !SMALL_KERNEL */

Generated by: LCOV version 1.13