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
63 int
sys_obreak(struct proc * p,void * v,register_t * retval)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
uvm_grow(struct proc * p,vaddr_t sp)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
uvm_coredump_walk_amap(struct vm_map_entry * entry,int * nsegmentp,uvm_coredump_walk_cb * walk,void * cookie)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 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 */
221 static inline int
uvm_should_coredump(struct proc * p,struct vm_map_entry * entry)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) &&
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() */
258 static int
noop(vaddr_t start,vaddr_t realend,vaddr_t end,vm_prot_t prot,int isvnode,int nsegment,void * cookie)259 noop(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
274 int
uvm_coredump_walkmap(struct proc * p,uvm_coredump_setup_cb * setup,uvm_coredump_walk_cb * walk,void * cookie)275 uvm_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) {
370 cleanup:
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