xref: /openbsd-src/sys/kern/kern_resource.c (revision 9ece112b761b1c2d5104e57320adb748e0eaebe5)
1 /*	$OpenBSD: kern_resource.c,v 1.38 2012/03/19 09:05:39 guenther Exp $	*/
2 /*	$NetBSD: kern_resource.c,v 1.38 1996/10/23 07:19:38 matthias Exp $	*/
3 
4 /*-
5  * Copyright (c) 1982, 1986, 1991, 1993
6  *	The Regents of the University of California.  All rights reserved.
7  * (c) UNIX System Laboratories, Inc.
8  * All or some portions of this file are derived from material licensed
9  * to the University of California by American Telephone and Telegraph
10  * Co. or Unix System Laboratories, Inc. and are reproduced herein with
11  * the permission of UNIX System Laboratories, Inc.
12  *
13  * Redistribution and use in source and binary forms, with or without
14  * modification, are permitted provided that the following conditions
15  * are met:
16  * 1. Redistributions of source code must retain the above copyright
17  *    notice, this list of conditions and the following disclaimer.
18  * 2. Redistributions in binary form must reproduce the above copyright
19  *    notice, this list of conditions and the following disclaimer in the
20  *    documentation and/or other materials provided with the distribution.
21  * 3. Neither the name of the University nor the names of its contributors
22  *    may be used to endorse or promote products derived from this software
23  *    without specific prior written permission.
24  *
25  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
26  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
27  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
28  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
29  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
30  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
31  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
32  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
33  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
34  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
35  * SUCH DAMAGE.
36  *
37  *	@(#)kern_resource.c	8.5 (Berkeley) 1/21/94
38  */
39 
40 #include <sys/param.h>
41 #include <sys/systm.h>
42 #include <sys/kernel.h>
43 #include <sys/file.h>
44 #include <sys/resourcevar.h>
45 #include <sys/pool.h>
46 #include <sys/proc.h>
47 #include <sys/ktrace.h>
48 #include <sys/sched.h>
49 
50 #include <sys/mount.h>
51 #include <sys/syscallargs.h>
52 
53 #include <uvm/uvm_extern.h>
54 
55 /*
56  * Patchable maximum data and stack limits.
57  */
58 rlim_t maxdmap = MAXDSIZ;
59 rlim_t maxsmap = MAXSSIZ;
60 
61 /*
62  * Resource controls and accounting.
63  */
64 
65 int
66 sys_getpriority(struct proc *curp, void *v, register_t *retval)
67 {
68 	struct sys_getpriority_args /* {
69 		syscallarg(int) which;
70 		syscallarg(id_t) who;
71 	} */ *uap = v;
72 	struct process *pr;
73 	struct proc *p;
74 	int low = NZERO + PRIO_MAX + 1;
75 
76 	switch (SCARG(uap, which)) {
77 
78 	case PRIO_PROCESS:
79 		if (SCARG(uap, who) == 0)
80 			pr = curp->p_p;
81 		else
82 			pr = prfind(SCARG(uap, who));
83 		if (pr == NULL)
84 			break;
85 		if (pr->ps_nice < low)
86 			low = pr->ps_nice;
87 		break;
88 
89 	case PRIO_PGRP: {
90 		struct pgrp *pg;
91 
92 		if (SCARG(uap, who) == 0)
93 			pg = curp->p_p->ps_pgrp;
94 		else if ((pg = pgfind(SCARG(uap, who))) == NULL)
95 			break;
96 		LIST_FOREACH(pr, &pg->pg_members, ps_pglist)
97 			if (pr->ps_nice < low)
98 				low = pr->ps_nice;
99 		break;
100 	}
101 
102 	case PRIO_USER:
103 		if (SCARG(uap, who) == 0)
104 			SCARG(uap, who) = curp->p_ucred->cr_uid;
105 		LIST_FOREACH(p, &allproc, p_list)
106 			if ((p->p_flag & P_THREAD) == 0 &&
107 			    p->p_ucred->cr_uid == SCARG(uap, who) &&
108 			    p->p_p->ps_nice < low)
109 				low = p->p_p->ps_nice;
110 		break;
111 
112 	default:
113 		return (EINVAL);
114 	}
115 	if (low == NZERO + PRIO_MAX + 1)
116 		return (ESRCH);
117 	*retval = low - NZERO;
118 	return (0);
119 }
120 
121 /* ARGSUSED */
122 int
123 sys_setpriority(struct proc *curp, void *v, register_t *retval)
124 {
125 	struct sys_setpriority_args /* {
126 		syscallarg(int) which;
127 		syscallarg(id_t) who;
128 		syscallarg(int) prio;
129 	} */ *uap = v;
130 	struct process *pr;
131 	int found = 0, error = 0;
132 
133 	switch (SCARG(uap, which)) {
134 
135 	case PRIO_PROCESS:
136 		if (SCARG(uap, who) == 0)
137 			pr = curp->p_p;
138 		else
139 			pr = prfind(SCARG(uap, who));
140 		if (pr == NULL)
141 			break;
142 		error = donice(curp, pr, SCARG(uap, prio));
143 		found++;
144 		break;
145 
146 	case PRIO_PGRP: {
147 		struct pgrp *pg;
148 
149 		if (SCARG(uap, who) == 0)
150 			pg = curp->p_p->ps_pgrp;
151 		else if ((pg = pgfind(SCARG(uap, who))) == NULL)
152 			break;
153 		LIST_FOREACH(pr, &pg->pg_members, ps_pglist) {
154 			error = donice(curp, pr, SCARG(uap, prio));
155 			found++;
156 		}
157 		break;
158 	}
159 
160 	case PRIO_USER: {
161 		struct proc *p;
162 		if (SCARG(uap, who) == 0)
163 			SCARG(uap, who) = curp->p_ucred->cr_uid;
164 		LIST_FOREACH(p, &allproc, p_list)
165 			if ((p->p_flag & P_THREAD) == 0 &&
166 			    p->p_ucred->cr_uid == SCARG(uap, who)) {
167 				error = donice(curp, p->p_p, SCARG(uap, prio));
168 				found++;
169 			}
170 		break;
171 	}
172 
173 	default:
174 		return (EINVAL);
175 	}
176 	if (found == 0)
177 		return (ESRCH);
178 	return (error);
179 }
180 
181 int
182 donice(struct proc *curp, struct process *chgpr, int n)
183 {
184 	struct pcred *pcred = curp->p_cred;
185 	struct proc *p;
186 	int s;
187 
188 	if (pcred->pc_ucred->cr_uid && pcred->p_ruid &&
189 	    pcred->pc_ucred->cr_uid != chgpr->ps_cred->pc_ucred->cr_uid &&
190 	    pcred->p_ruid != chgpr->ps_cred->pc_ucred->cr_uid)
191 		return (EPERM);
192 	if (n > PRIO_MAX)
193 		n = PRIO_MAX;
194 	if (n < PRIO_MIN)
195 		n = PRIO_MIN;
196 	n += NZERO;
197 	if (n < chgpr->ps_nice && suser(curp, 0))
198 		return (EACCES);
199 	chgpr->ps_nice = n;
200 	SCHED_LOCK(s);
201 	TAILQ_FOREACH(p, &chgpr->ps_threads, p_thr_link)
202 		(void)resetpriority(p);
203 	SCHED_UNLOCK(s);
204 	return (0);
205 }
206 
207 /* ARGSUSED */
208 int
209 sys_setrlimit(struct proc *p, void *v, register_t *retval)
210 {
211 	struct sys_setrlimit_args /* {
212 		syscallarg(int) which;
213 		syscallarg(const struct rlimit *) rlp;
214 	} */ *uap = v;
215 	struct rlimit alim;
216 	int error;
217 
218 	error = copyin((caddr_t)SCARG(uap, rlp), (caddr_t)&alim,
219 		       sizeof (struct rlimit));
220 	if (error)
221 		return (error);
222 #ifdef KTRACE
223 	if (KTRPOINT(p, KTR_STRUCT))
224 		ktrrlimit(p, &alim);
225 #endif
226 	return (dosetrlimit(p, SCARG(uap, which), &alim));
227 }
228 
229 int
230 dosetrlimit(struct proc *p, u_int which, struct rlimit *limp)
231 {
232 	struct rlimit *alimp;
233 	rlim_t maxlim;
234 	int error;
235 
236 	if (which >= RLIM_NLIMITS)
237 		return (EINVAL);
238 
239 	alimp = &p->p_rlimit[which];
240 	if (limp->rlim_cur > alimp->rlim_max ||
241 	    limp->rlim_max > alimp->rlim_max)
242 		if ((error = suser(p, 0)) != 0)
243 			return (error);
244 	if (p->p_p->ps_limit->p_refcnt > 1) {
245 		struct plimit *l = p->p_p->ps_limit;
246 
247 		/* limcopy() can sleep, so copy before decrementing refcnt */
248 		p->p_p->ps_limit = limcopy(l);
249 		l->p_refcnt--;
250 		alimp = &p->p_rlimit[which];
251 	}
252 
253 	switch (which) {
254 	case RLIMIT_DATA:
255 		maxlim = maxdmap;
256 		break;
257 	case RLIMIT_STACK:
258 		maxlim = maxsmap;
259 		break;
260 	case RLIMIT_NOFILE:
261 		maxlim = maxfiles;
262 		break;
263 	case RLIMIT_NPROC:
264 		maxlim = maxproc;
265 		break;
266 	default:
267 		maxlim = RLIM_INFINITY;
268 		break;
269 	}
270 
271 	if (limp->rlim_max > maxlim)
272 		limp->rlim_max = maxlim;
273 	if (limp->rlim_cur > limp->rlim_max)
274 		limp->rlim_cur = limp->rlim_max;
275 
276 	if (which == RLIMIT_STACK) {
277 		/*
278 		 * Stack is allocated to the max at exec time with only
279 		 * "rlim_cur" bytes accessible.  If stack limit is going
280 		 * up make more accessible, if going down make inaccessible.
281 		 */
282 		if (limp->rlim_cur != alimp->rlim_cur) {
283 			vaddr_t addr;
284 			vsize_t size;
285 			vm_prot_t prot;
286 
287 			if (limp->rlim_cur > alimp->rlim_cur) {
288 				prot = VM_PROT_READ|VM_PROT_WRITE;
289 				size = limp->rlim_cur - alimp->rlim_cur;
290 #ifdef MACHINE_STACK_GROWS_UP
291 				addr = USRSTACK + alimp->rlim_cur;
292 #else
293 				addr = USRSTACK - limp->rlim_cur;
294 #endif
295 			} else {
296 				prot = VM_PROT_NONE;
297 				size = alimp->rlim_cur - limp->rlim_cur;
298 #ifdef MACHINE_STACK_GROWS_UP
299 				addr = USRSTACK + limp->rlim_cur;
300 #else
301 				addr = USRSTACK - alimp->rlim_cur;
302 #endif
303 			}
304 			addr = trunc_page(addr);
305 			size = round_page(size);
306 			(void) uvm_map_protect(&p->p_vmspace->vm_map,
307 					      addr, addr+size, prot, FALSE);
308 		}
309 	}
310 
311 	*alimp = *limp;
312 	return (0);
313 }
314 
315 /* ARGSUSED */
316 int
317 sys_getrlimit(struct proc *p, void *v, register_t *retval)
318 {
319 	struct sys_getrlimit_args /* {
320 		syscallarg(int) which;
321 		syscallarg(struct rlimit *) rlp;
322 	} */ *uap = v;
323 	struct rlimit *alimp;
324 	int error;
325 
326 	if (SCARG(uap, which) < 0 || SCARG(uap, which) >= RLIM_NLIMITS)
327 		return (EINVAL);
328 	alimp = &p->p_rlimit[SCARG(uap, which)];
329 	error = copyout(alimp, SCARG(uap, rlp), sizeof(struct rlimit));
330 #ifdef KTRACE
331 	if (error == 0 && KTRPOINT(p, KTR_STRUCT))
332 		ktrrlimit(p, alimp);
333 #endif
334 	return (error);
335 }
336 
337 /*
338  * Transform the running time and tick information in proc p into user,
339  * system, and interrupt time usage.
340  */
341 void
342 calcru(struct proc *p, struct timeval *up, struct timeval *sp,
343     struct timeval *ip)
344 {
345 	u_quad_t st, ut, it;
346 	int freq;
347 	int s;
348 
349 	s = splstatclock();
350 	st = p->p_sticks;
351 	ut = p->p_uticks;
352 	it = p->p_iticks;
353 	splx(s);
354 
355 	if (st + ut + it == 0) {
356 		timerclear(up);
357 		timerclear(sp);
358 		if (ip != NULL)
359 			timerclear(ip);
360 		return;
361 	}
362 
363 	freq = stathz ? stathz : hz;
364 
365 	st = st * 1000000 / freq;
366 	sp->tv_sec = st / 1000000;
367 	sp->tv_usec = st % 1000000;
368 	ut = ut * 1000000 / freq;
369 	up->tv_sec = ut / 1000000;
370 	up->tv_usec = ut % 1000000;
371 	if (ip != NULL) {
372 		it = it * 1000000 / freq;
373 		ip->tv_sec = it / 1000000;
374 		ip->tv_usec = it % 1000000;
375 	}
376 }
377 
378 /* ARGSUSED */
379 int
380 sys_getrusage(struct proc *p, void *v, register_t *retval)
381 {
382 	struct sys_getrusage_args /* {
383 		syscallarg(int) who;
384 		syscallarg(struct rusage *) rusage;
385 	} */ *uap = v;
386 	struct process *pr = p->p_p;
387 	struct rusage ru;
388 	struct rusage *rup;
389 
390 	switch (SCARG(uap, who)) {
391 
392 	case RUSAGE_SELF:
393 		calcru(p, &p->p_stats->p_ru.ru_utime,
394 		    &p->p_stats->p_ru.ru_stime, NULL);
395 		ru = p->p_stats->p_ru;
396 		rup = &ru;
397 
398 		/* XXX add on already dead threads */
399 
400 		/* add on other living threads */
401 		{
402 			struct proc *q;
403 
404 			TAILQ_FOREACH(q, &pr->ps_threads, p_thr_link) {
405 				if (q == p || P_ZOMBIE(q))
406 					continue;
407 				/*
408 				 * XXX this is approximate: no call
409 				 * to calcru in other running threads
410 				 */
411 				ruadd(rup, &q->p_stats->p_ru);
412 			}
413 		}
414 		break;
415 
416 	case RUSAGE_THREAD:
417 		rup = &p->p_stats->p_ru;
418 		calcru(p, &rup->ru_utime, &rup->ru_stime, NULL);
419 		ru = *rup;
420 		break;
421 
422 	case RUSAGE_CHILDREN:
423 		rup = &p->p_stats->p_cru;
424 		break;
425 
426 	default:
427 		return (EINVAL);
428 	}
429 	return (copyout((caddr_t)rup, (caddr_t)SCARG(uap, rusage),
430 	    sizeof (struct rusage)));
431 }
432 
433 void
434 ruadd(struct rusage *ru, struct rusage *ru2)
435 {
436 	long *ip, *ip2;
437 	int i;
438 
439 	timeradd(&ru->ru_utime, &ru2->ru_utime, &ru->ru_utime);
440 	timeradd(&ru->ru_stime, &ru2->ru_stime, &ru->ru_stime);
441 	if (ru->ru_maxrss < ru2->ru_maxrss)
442 		ru->ru_maxrss = ru2->ru_maxrss;
443 	ip = &ru->ru_first; ip2 = &ru2->ru_first;
444 	for (i = &ru->ru_last - &ru->ru_first; i >= 0; i--)
445 		*ip++ += *ip2++;
446 }
447 
448 struct pool plimit_pool;
449 
450 /*
451  * Make a copy of the plimit structure.
452  * We share these structures copy-on-write after fork,
453  * and copy when a limit is changed.
454  */
455 struct plimit *
456 limcopy(struct plimit *lim)
457 {
458 	struct plimit *newlim;
459 	static int initialized;
460 
461 	if (!initialized) {
462 		pool_init(&plimit_pool, sizeof(struct plimit), 0, 0, 0,
463 		    "plimitpl", &pool_allocator_nointr);
464 		initialized = 1;
465 	}
466 
467 	newlim = pool_get(&plimit_pool, PR_WAITOK);
468 	bcopy(lim->pl_rlimit, newlim->pl_rlimit,
469 	    sizeof(struct rlimit) * RLIM_NLIMITS);
470 	newlim->p_refcnt = 1;
471 	return (newlim);
472 }
473 
474 void
475 limfree(struct plimit *lim)
476 {
477 	if (--lim->p_refcnt > 0)
478 		return;
479 	pool_put(&plimit_pool, lim);
480 }
481