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 */
|