xref: /openbsd-src/sys/kern/kern_resource.c (revision d4b60c8b5d9a82508a30538791acf0666ac75a35)
1 /*	$OpenBSD: kern_resource.c,v 1.37 2011/03/07 07:07:13 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/sched.h>
48 
49 #include <sys/mount.h>
50 #include <sys/syscallargs.h>
51 
52 #include <uvm/uvm_extern.h>
53 
54 /*
55  * Patchable maximum data and stack limits.
56  */
57 rlim_t maxdmap = MAXDSIZ;
58 rlim_t maxsmap = MAXSSIZ;
59 
60 /*
61  * Resource controls and accounting.
62  */
63 
64 int
65 sys_getpriority(struct proc *curp, void *v, register_t *retval)
66 {
67 	struct sys_getpriority_args /* {
68 		syscallarg(int) which;
69 		syscallarg(id_t) who;
70 	} */ *uap = v;
71 	struct process *pr;
72 	struct proc *p;
73 	int low = NZERO + PRIO_MAX + 1;
74 
75 	switch (SCARG(uap, which)) {
76 
77 	case PRIO_PROCESS:
78 		if (SCARG(uap, who) == 0)
79 			pr = curp->p_p;
80 		else
81 			pr = prfind(SCARG(uap, who));
82 		if (pr == NULL)
83 			break;
84 		if (pr->ps_nice < low)
85 			low = pr->ps_nice;
86 		break;
87 
88 	case PRIO_PGRP: {
89 		struct pgrp *pg;
90 
91 		if (SCARG(uap, who) == 0)
92 			pg = curp->p_p->ps_pgrp;
93 		else if ((pg = pgfind(SCARG(uap, who))) == NULL)
94 			break;
95 		LIST_FOREACH(pr, &pg->pg_members, ps_pglist)
96 			if (pr->ps_nice < low)
97 				low = pr->ps_nice;
98 		break;
99 	}
100 
101 	case PRIO_USER:
102 		if (SCARG(uap, who) == 0)
103 			SCARG(uap, who) = curp->p_ucred->cr_uid;
104 		LIST_FOREACH(p, &allproc, p_list)
105 			if ((p->p_flag & P_THREAD) == 0 &&
106 			    p->p_ucred->cr_uid == SCARG(uap, who) &&
107 			    p->p_p->ps_nice < low)
108 				low = p->p_p->ps_nice;
109 		break;
110 
111 	default:
112 		return (EINVAL);
113 	}
114 	if (low == NZERO + PRIO_MAX + 1)
115 		return (ESRCH);
116 	*retval = low - NZERO;
117 	return (0);
118 }
119 
120 /* ARGSUSED */
121 int
122 sys_setpriority(struct proc *curp, void *v, register_t *retval)
123 {
124 	struct sys_setpriority_args /* {
125 		syscallarg(int) which;
126 		syscallarg(id_t) who;
127 		syscallarg(int) prio;
128 	} */ *uap = v;
129 	struct process *pr;
130 	int found = 0, error = 0;
131 
132 	switch (SCARG(uap, which)) {
133 
134 	case PRIO_PROCESS:
135 		if (SCARG(uap, who) == 0)
136 			pr = curp->p_p;
137 		else
138 			pr = prfind(SCARG(uap, who));
139 		if (pr == NULL)
140 			break;
141 		error = donice(curp, pr, SCARG(uap, prio));
142 		found++;
143 		break;
144 
145 	case PRIO_PGRP: {
146 		struct pgrp *pg;
147 
148 		if (SCARG(uap, who) == 0)
149 			pg = curp->p_p->ps_pgrp;
150 		else if ((pg = pgfind(SCARG(uap, who))) == NULL)
151 			break;
152 		LIST_FOREACH(pr, &pg->pg_members, ps_pglist) {
153 			error = donice(curp, pr, SCARG(uap, prio));
154 			found++;
155 		}
156 		break;
157 	}
158 
159 	case PRIO_USER: {
160 		struct proc *p;
161 		if (SCARG(uap, who) == 0)
162 			SCARG(uap, who) = curp->p_ucred->cr_uid;
163 		LIST_FOREACH(p, &allproc, p_list)
164 			if ((p->p_flag & P_THREAD) == 0 &&
165 			    p->p_ucred->cr_uid == SCARG(uap, who)) {
166 				error = donice(curp, p->p_p, SCARG(uap, prio));
167 				found++;
168 			}
169 		break;
170 	}
171 
172 	default:
173 		return (EINVAL);
174 	}
175 	if (found == 0)
176 		return (ESRCH);
177 	return (error);
178 }
179 
180 int
181 donice(struct proc *curp, struct process *chgpr, int n)
182 {
183 	struct pcred *pcred = curp->p_cred;
184 	struct proc *p;
185 	int s;
186 
187 	if (pcred->pc_ucred->cr_uid && pcred->p_ruid &&
188 	    pcred->pc_ucred->cr_uid != chgpr->ps_cred->pc_ucred->cr_uid &&
189 	    pcred->p_ruid != chgpr->ps_cred->pc_ucred->cr_uid)
190 		return (EPERM);
191 	if (n > PRIO_MAX)
192 		n = PRIO_MAX;
193 	if (n < PRIO_MIN)
194 		n = PRIO_MIN;
195 	n += NZERO;
196 	if (n < chgpr->ps_nice && suser(curp, 0))
197 		return (EACCES);
198 	chgpr->ps_nice = n;
199 	SCHED_LOCK(s);
200 	TAILQ_FOREACH(p, &chgpr->ps_threads, p_thr_link)
201 		(void)resetpriority(p);
202 	SCHED_UNLOCK(s);
203 	return (0);
204 }
205 
206 /* ARGSUSED */
207 int
208 sys_setrlimit(struct proc *p, void *v, register_t *retval)
209 {
210 	struct sys_setrlimit_args /* {
211 		syscallarg(int) which;
212 		syscallarg(const struct rlimit *) rlp;
213 	} */ *uap = v;
214 	struct rlimit alim;
215 	int error;
216 
217 	error = copyin((caddr_t)SCARG(uap, rlp), (caddr_t)&alim,
218 		       sizeof (struct rlimit));
219 	if (error)
220 		return (error);
221 	return (dosetrlimit(p, SCARG(uap, which), &alim));
222 }
223 
224 int
225 dosetrlimit(struct proc *p, u_int which, struct rlimit *limp)
226 {
227 	struct rlimit *alimp;
228 	rlim_t maxlim;
229 	int error;
230 
231 	if (which >= RLIM_NLIMITS)
232 		return (EINVAL);
233 
234 	alimp = &p->p_rlimit[which];
235 	if (limp->rlim_cur > alimp->rlim_max ||
236 	    limp->rlim_max > alimp->rlim_max)
237 		if ((error = suser(p, 0)) != 0)
238 			return (error);
239 	if (p->p_p->ps_limit->p_refcnt > 1) {
240 		struct plimit *l = p->p_p->ps_limit;
241 
242 		/* limcopy() can sleep, so copy before decrementing refcnt */
243 		p->p_p->ps_limit = limcopy(l);
244 		l->p_refcnt--;
245 		alimp = &p->p_rlimit[which];
246 	}
247 
248 	switch (which) {
249 	case RLIMIT_DATA:
250 		maxlim = maxdmap;
251 		break;
252 	case RLIMIT_STACK:
253 		maxlim = maxsmap;
254 		break;
255 	case RLIMIT_NOFILE:
256 		maxlim = maxfiles;
257 		break;
258 	case RLIMIT_NPROC:
259 		maxlim = maxproc;
260 		break;
261 	default:
262 		maxlim = RLIM_INFINITY;
263 		break;
264 	}
265 
266 	if (limp->rlim_max > maxlim)
267 		limp->rlim_max = maxlim;
268 	if (limp->rlim_cur > limp->rlim_max)
269 		limp->rlim_cur = limp->rlim_max;
270 
271 	if (which == RLIMIT_STACK) {
272 		/*
273 		 * Stack is allocated to the max at exec time with only
274 		 * "rlim_cur" bytes accessible.  If stack limit is going
275 		 * up make more accessible, if going down make inaccessible.
276 		 */
277 		if (limp->rlim_cur != alimp->rlim_cur) {
278 			vaddr_t addr;
279 			vsize_t size;
280 			vm_prot_t prot;
281 
282 			if (limp->rlim_cur > alimp->rlim_cur) {
283 				prot = VM_PROT_READ|VM_PROT_WRITE;
284 				size = limp->rlim_cur - alimp->rlim_cur;
285 #ifdef MACHINE_STACK_GROWS_UP
286 				addr = USRSTACK + alimp->rlim_cur;
287 #else
288 				addr = USRSTACK - limp->rlim_cur;
289 #endif
290 			} else {
291 				prot = VM_PROT_NONE;
292 				size = alimp->rlim_cur - limp->rlim_cur;
293 #ifdef MACHINE_STACK_GROWS_UP
294 				addr = USRSTACK + limp->rlim_cur;
295 #else
296 				addr = USRSTACK - alimp->rlim_cur;
297 #endif
298 			}
299 			addr = trunc_page(addr);
300 			size = round_page(size);
301 			(void) uvm_map_protect(&p->p_vmspace->vm_map,
302 					      addr, addr+size, prot, FALSE);
303 		}
304 	}
305 
306 	*alimp = *limp;
307 	return (0);
308 }
309 
310 /* ARGSUSED */
311 int
312 sys_getrlimit(struct proc *p, void *v, register_t *retval)
313 {
314 	struct sys_getrlimit_args /* {
315 		syscallarg(int) which;
316 		syscallarg(struct rlimit *) rlp;
317 	} */ *uap = v;
318 
319 	if (SCARG(uap, which) < 0 || SCARG(uap, which) >= RLIM_NLIMITS)
320 		return (EINVAL);
321 	return (copyout((caddr_t)&p->p_rlimit[SCARG(uap, which)],
322 	    (caddr_t)SCARG(uap, rlp), sizeof (struct rlimit)));
323 }
324 
325 /*
326  * Transform the running time and tick information in proc p into user,
327  * system, and interrupt time usage.
328  */
329 void
330 calcru(struct proc *p, struct timeval *up, struct timeval *sp,
331     struct timeval *ip)
332 {
333 	u_quad_t st, ut, it;
334 	int freq;
335 	int s;
336 
337 	s = splstatclock();
338 	st = p->p_sticks;
339 	ut = p->p_uticks;
340 	it = p->p_iticks;
341 	splx(s);
342 
343 	if (st + ut + it == 0) {
344 		timerclear(up);
345 		timerclear(sp);
346 		if (ip != NULL)
347 			timerclear(ip);
348 		return;
349 	}
350 
351 	freq = stathz ? stathz : hz;
352 
353 	st = st * 1000000 / freq;
354 	sp->tv_sec = st / 1000000;
355 	sp->tv_usec = st % 1000000;
356 	ut = ut * 1000000 / freq;
357 	up->tv_sec = ut / 1000000;
358 	up->tv_usec = ut % 1000000;
359 	if (ip != NULL) {
360 		it = it * 1000000 / freq;
361 		ip->tv_sec = it / 1000000;
362 		ip->tv_usec = it % 1000000;
363 	}
364 }
365 
366 /* ARGSUSED */
367 int
368 sys_getrusage(struct proc *p, void *v, register_t *retval)
369 {
370 	struct sys_getrusage_args /* {
371 		syscallarg(int) who;
372 		syscallarg(struct rusage *) rusage;
373 	} */ *uap = v;
374 	struct process *pr = p->p_p;
375 	struct rusage ru;
376 	struct rusage *rup;
377 
378 	switch (SCARG(uap, who)) {
379 
380 	case RUSAGE_SELF:
381 		calcru(p, &p->p_stats->p_ru.ru_utime,
382 		    &p->p_stats->p_ru.ru_stime, NULL);
383 		ru = p->p_stats->p_ru;
384 		rup = &ru;
385 
386 		/* XXX add on already dead threads */
387 
388 		/* add on other living threads */
389 		{
390 			struct proc *q;
391 
392 			TAILQ_FOREACH(q, &pr->ps_threads, p_thr_link) {
393 				if (q == p || P_ZOMBIE(q))
394 					continue;
395 				/*
396 				 * XXX this is approximate: no call
397 				 * to calcru in other running threads
398 				 */
399 				ruadd(rup, &q->p_stats->p_ru);
400 			}
401 		}
402 		break;
403 
404 	case RUSAGE_THREAD:
405 		rup = &p->p_stats->p_ru;
406 		calcru(p, &rup->ru_utime, &rup->ru_stime, NULL);
407 		ru = *rup;
408 		break;
409 
410 	case RUSAGE_CHILDREN:
411 		rup = &p->p_stats->p_cru;
412 		break;
413 
414 	default:
415 		return (EINVAL);
416 	}
417 	return (copyout((caddr_t)rup, (caddr_t)SCARG(uap, rusage),
418 	    sizeof (struct rusage)));
419 }
420 
421 void
422 ruadd(struct rusage *ru, struct rusage *ru2)
423 {
424 	long *ip, *ip2;
425 	int i;
426 
427 	timeradd(&ru->ru_utime, &ru2->ru_utime, &ru->ru_utime);
428 	timeradd(&ru->ru_stime, &ru2->ru_stime, &ru->ru_stime);
429 	if (ru->ru_maxrss < ru2->ru_maxrss)
430 		ru->ru_maxrss = ru2->ru_maxrss;
431 	ip = &ru->ru_first; ip2 = &ru2->ru_first;
432 	for (i = &ru->ru_last - &ru->ru_first; i >= 0; i--)
433 		*ip++ += *ip2++;
434 }
435 
436 struct pool plimit_pool;
437 
438 /*
439  * Make a copy of the plimit structure.
440  * We share these structures copy-on-write after fork,
441  * and copy when a limit is changed.
442  */
443 struct plimit *
444 limcopy(struct plimit *lim)
445 {
446 	struct plimit *newlim;
447 	static int initialized;
448 
449 	if (!initialized) {
450 		pool_init(&plimit_pool, sizeof(struct plimit), 0, 0, 0,
451 		    "plimitpl", &pool_allocator_nointr);
452 		initialized = 1;
453 	}
454 
455 	newlim = pool_get(&plimit_pool, PR_WAITOK);
456 	bcopy(lim->pl_rlimit, newlim->pl_rlimit,
457 	    sizeof(struct rlimit) * RLIM_NLIMITS);
458 	newlim->p_refcnt = 1;
459 	return (newlim);
460 }
461 
462 void
463 limfree(struct plimit *lim)
464 {
465 	if (--lim->p_refcnt > 0)
466 		return;
467 	pool_put(&plimit_pool, lim);
468 }
469