kern_resource.c revision 1.51
1/* $OpenBSD: kern_resource.c,v 1.51 2014/11/16 12:31:00 deraadt 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 55void tuagg_sub(struct tusage *, struct proc *); 56 57/* 58 * Patchable maximum data and stack limits. 59 */ 60rlim_t maxdmap = MAXDSIZ; 61rlim_t maxsmap = MAXSSIZ; 62 63/* 64 * Resource controls and accounting. 65 */ 66 67int 68sys_getpriority(struct proc *curp, void *v, register_t *retval) 69{ 70 struct sys_getpriority_args /* { 71 syscallarg(int) which; 72 syscallarg(id_t) who; 73 } */ *uap = v; 74 struct process *pr; 75 int low = NZERO + PRIO_MAX + 1; 76 77 switch (SCARG(uap, which)) { 78 79 case PRIO_PROCESS: 80 if (SCARG(uap, who) == 0) 81 pr = curp->p_p; 82 else 83 pr = prfind(SCARG(uap, who)); 84 if (pr == NULL) 85 break; 86 if (pr->ps_nice < low) 87 low = pr->ps_nice; 88 break; 89 90 case PRIO_PGRP: { 91 struct pgrp *pg; 92 93 if (SCARG(uap, who) == 0) 94 pg = curp->p_p->ps_pgrp; 95 else if ((pg = pgfind(SCARG(uap, who))) == NULL) 96 break; 97 LIST_FOREACH(pr, &pg->pg_members, ps_pglist) 98 if (pr->ps_nice < low) 99 low = pr->ps_nice; 100 break; 101 } 102 103 case PRIO_USER: 104 if (SCARG(uap, who) == 0) 105 SCARG(uap, who) = curp->p_ucred->cr_uid; 106 LIST_FOREACH(pr, &allprocess, ps_list) 107 if (pr->ps_ucred->cr_uid == SCARG(uap, who) && 108 pr->ps_nice < low) 109 low = pr->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 */ 122int 123sys_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 if (SCARG(uap, who) == 0) 162 SCARG(uap, who) = curp->p_ucred->cr_uid; 163 LIST_FOREACH(pr, &allprocess, ps_list) 164 if (pr->ps_ucred->cr_uid == SCARG(uap, who)) { 165 error = donice(curp, pr, SCARG(uap, prio)); 166 found++; 167 } 168 break; 169 170 default: 171 return (EINVAL); 172 } 173 if (found == 0) 174 return (ESRCH); 175 return (error); 176} 177 178int 179donice(struct proc *curp, struct process *chgpr, int n) 180{ 181 struct ucred *ucred = curp->p_ucred; 182 struct proc *p; 183 int s; 184 185 if (ucred->cr_uid != 0 && ucred->cr_ruid != 0 && 186 ucred->cr_uid != chgpr->ps_ucred->cr_uid && 187 ucred->cr_ruid != chgpr->ps_ucred->cr_uid) 188 return (EPERM); 189 if (n > PRIO_MAX) 190 n = PRIO_MAX; 191 if (n < PRIO_MIN) 192 n = PRIO_MIN; 193 n += NZERO; 194 if (n < chgpr->ps_nice && suser(curp, 0)) 195 return (EACCES); 196 chgpr->ps_nice = n; 197 SCHED_LOCK(s); 198 TAILQ_FOREACH(p, &chgpr->ps_threads, p_thr_link) 199 (void)resetpriority(p); 200 SCHED_UNLOCK(s); 201 return (0); 202} 203 204/* ARGSUSED */ 205int 206sys_setrlimit(struct proc *p, void *v, register_t *retval) 207{ 208 struct sys_setrlimit_args /* { 209 syscallarg(int) which; 210 syscallarg(const struct rlimit *) rlp; 211 } */ *uap = v; 212 struct rlimit alim; 213 int error; 214 215 error = copyin((caddr_t)SCARG(uap, rlp), (caddr_t)&alim, 216 sizeof (struct rlimit)); 217 if (error) 218 return (error); 219#ifdef KTRACE 220 if (KTRPOINT(p, KTR_STRUCT)) 221 ktrrlimit(p, &alim); 222#endif 223 return (dosetrlimit(p, SCARG(uap, which), &alim)); 224} 225 226int 227dosetrlimit(struct proc *p, u_int which, struct rlimit *limp) 228{ 229 struct rlimit *alimp; 230 rlim_t maxlim; 231 int error; 232 233 if (which >= RLIM_NLIMITS || limp->rlim_cur > limp->rlim_max) 234 return (EINVAL); 235 236 alimp = &p->p_rlimit[which]; 237 if (limp->rlim_max > alimp->rlim_max) 238 if ((error = suser(p, 0)) != 0) 239 return (error); 240 if (p->p_p->ps_limit->p_refcnt > 1) { 241 struct plimit *l = p->p_p->ps_limit; 242 243 /* limcopy() can sleep, so copy before decrementing refcnt */ 244 p->p_p->ps_limit = limcopy(l); 245 limfree(l); 246 alimp = &p->p_rlimit[which]; 247 } 248 249 switch (which) { 250 case RLIMIT_DATA: 251 maxlim = maxdmap; 252 break; 253 case RLIMIT_STACK: 254 maxlim = maxsmap; 255 break; 256 case RLIMIT_NOFILE: 257 maxlim = maxfiles; 258 break; 259 case RLIMIT_NPROC: 260 maxlim = maxprocess; 261 break; 262 default: 263 maxlim = RLIM_INFINITY; 264 break; 265 } 266 267 if (limp->rlim_max > maxlim) 268 limp->rlim_max = maxlim; 269 if (limp->rlim_cur > limp->rlim_max) 270 limp->rlim_cur = limp->rlim_max; 271 272 if (which == RLIMIT_STACK) { 273 /* 274 * Stack is allocated to the max at exec time with only 275 * "rlim_cur" bytes accessible. If stack limit is going 276 * up make more accessible, if going down make inaccessible. 277 */ 278 if (limp->rlim_cur != alimp->rlim_cur) { 279 vaddr_t addr; 280 vsize_t size; 281 vm_prot_t prot; 282 283 if (limp->rlim_cur > alimp->rlim_cur) { 284 prot = PROT_READ | PROT_WRITE; 285 size = limp->rlim_cur - alimp->rlim_cur; 286#ifdef MACHINE_STACK_GROWS_UP 287 addr = USRSTACK + alimp->rlim_cur; 288#else 289 addr = USRSTACK - limp->rlim_cur; 290#endif 291 } else { 292 prot = PROT_NONE; 293 size = alimp->rlim_cur - limp->rlim_cur; 294#ifdef MACHINE_STACK_GROWS_UP 295 addr = USRSTACK + limp->rlim_cur; 296#else 297 addr = USRSTACK - alimp->rlim_cur; 298#endif 299 } 300 addr = trunc_page(addr); 301 size = round_page(size); 302 (void) uvm_map_protect(&p->p_vmspace->vm_map, 303 addr, addr+size, prot, FALSE); 304 } 305 } 306 307 *alimp = *limp; 308 return (0); 309} 310 311/* ARGSUSED */ 312int 313sys_getrlimit(struct proc *p, void *v, register_t *retval) 314{ 315 struct sys_getrlimit_args /* { 316 syscallarg(int) which; 317 syscallarg(struct rlimit *) rlp; 318 } */ *uap = v; 319 struct rlimit *alimp; 320 int error; 321 322 if (SCARG(uap, which) < 0 || SCARG(uap, which) >= RLIM_NLIMITS) 323 return (EINVAL); 324 alimp = &p->p_rlimit[SCARG(uap, which)]; 325 error = copyout(alimp, SCARG(uap, rlp), sizeof(struct rlimit)); 326#ifdef KTRACE 327 if (error == 0 && KTRPOINT(p, KTR_STRUCT)) 328 ktrrlimit(p, alimp); 329#endif 330 return (error); 331} 332 333void 334tuagg_sub(struct tusage *tup, struct proc *p) 335{ 336 timespecadd(&tup->tu_runtime, &p->p_rtime, &tup->tu_runtime); 337 tup->tu_uticks += p->p_uticks; 338 tup->tu_sticks += p->p_sticks; 339 tup->tu_iticks += p->p_iticks; 340} 341 342/* 343 * Aggregate a single thread's immediate time counts into the running 344 * totals for the thread and process 345 */ 346void 347tuagg_unlocked(struct process *pr, struct proc *p) 348{ 349 tuagg_sub(&pr->ps_tu, p); 350 tuagg_sub(&p->p_tu, p); 351 timespecclear(&p->p_rtime); 352 p->p_uticks = 0; 353 p->p_sticks = 0; 354 p->p_iticks = 0; 355} 356 357void 358tuagg(struct process *pr, struct proc *p) 359{ 360 int s; 361 362 SCHED_LOCK(s); 363 tuagg_unlocked(pr, p); 364 SCHED_UNLOCK(s); 365} 366 367/* 368 * Transform the running time and tick information in a struct tusage 369 * into user, system, and interrupt time usage. 370 */ 371void 372calctsru(struct tusage *tup, struct timespec *up, struct timespec *sp, 373 struct timespec *ip) 374{ 375 u_quad_t st, ut, it; 376 int freq; 377 378 st = tup->tu_sticks; 379 ut = tup->tu_uticks; 380 it = tup->tu_iticks; 381 382 if (st + ut + it == 0) { 383 timespecclear(up); 384 timespecclear(sp); 385 if (ip != NULL) 386 timespecclear(ip); 387 return; 388 } 389 390 freq = stathz ? stathz : hz; 391 392 st = st * 1000000000 / freq; 393 sp->tv_sec = st / 1000000000; 394 sp->tv_nsec = st % 1000000000; 395 ut = ut * 1000000000 / freq; 396 up->tv_sec = ut / 1000000000; 397 up->tv_nsec = ut % 1000000000; 398 if (ip != NULL) { 399 it = it * 1000000000 / freq; 400 ip->tv_sec = it / 1000000000; 401 ip->tv_nsec = it % 1000000000; 402 } 403} 404 405void 406calcru(struct tusage *tup, struct timeval *up, struct timeval *sp, 407 struct timeval *ip) 408{ 409 struct timespec u, s, i; 410 411 calctsru(tup, &u, &s, ip != NULL ? &i : NULL); 412 TIMESPEC_TO_TIMEVAL(up, &u); 413 TIMESPEC_TO_TIMEVAL(sp, &s); 414 if (ip != NULL) 415 TIMESPEC_TO_TIMEVAL(ip, &i); 416} 417 418/* ARGSUSED */ 419int 420sys_getrusage(struct proc *p, void *v, register_t *retval) 421{ 422 struct sys_getrusage_args /* { 423 syscallarg(int) who; 424 syscallarg(struct rusage *) rusage; 425 } */ *uap = v; 426 struct rusage ru; 427 int error; 428 429 error = dogetrusage(p, SCARG(uap, who), &ru); 430 if (error == 0) { 431 error = copyout(&ru, SCARG(uap, rusage), sizeof(ru)); 432#ifdef KTRACE 433 if (error == 0 && KTRPOINT(p, KTR_STRUCT)) 434 ktrrusage(p, &ru); 435#endif 436 } 437 return (error); 438} 439 440int 441dogetrusage(struct proc *p, int who, struct rusage *rup) 442{ 443 struct process *pr = p->p_p; 444 struct proc *q; 445 446 switch (who) { 447 448 case RUSAGE_SELF: 449 /* start with the sum of dead threads, if any */ 450 if (pr->ps_ru != NULL) 451 *rup = *pr->ps_ru; 452 else 453 memset(rup, 0, sizeof(*rup)); 454 455 /* add on all living threads */ 456 TAILQ_FOREACH(q, &pr->ps_threads, p_thr_link) { 457 ruadd(rup, &q->p_ru); 458 tuagg(pr, q); 459 } 460 461 calcru(&pr->ps_tu, &rup->ru_utime, &rup->ru_stime, NULL); 462 break; 463 464 case RUSAGE_THREAD: 465 *rup = p->p_ru; 466 calcru(&p->p_tu, &rup->ru_utime, &rup->ru_stime, NULL); 467 break; 468 469 case RUSAGE_CHILDREN: 470 *rup = pr->ps_cru; 471 break; 472 473 default: 474 return (EINVAL); 475 } 476 return (0); 477} 478 479void 480ruadd(struct rusage *ru, struct rusage *ru2) 481{ 482 long *ip, *ip2; 483 int i; 484 485 timeradd(&ru->ru_utime, &ru2->ru_utime, &ru->ru_utime); 486 timeradd(&ru->ru_stime, &ru2->ru_stime, &ru->ru_stime); 487 if (ru->ru_maxrss < ru2->ru_maxrss) 488 ru->ru_maxrss = ru2->ru_maxrss; 489 ip = &ru->ru_first; ip2 = &ru2->ru_first; 490 for (i = &ru->ru_last - &ru->ru_first; i >= 0; i--) 491 *ip++ += *ip2++; 492} 493 494struct pool plimit_pool; 495 496/* 497 * Make a copy of the plimit structure. 498 * We share these structures copy-on-write after fork, 499 * and copy when a limit is changed. 500 */ 501struct plimit * 502limcopy(struct plimit *lim) 503{ 504 struct plimit *newlim; 505 static int initialized; 506 507 if (!initialized) { 508 pool_init(&plimit_pool, sizeof(struct plimit), 0, 0, 0, 509 "plimitpl", &pool_allocator_nointr); 510 initialized = 1; 511 } 512 513 newlim = pool_get(&plimit_pool, PR_WAITOK); 514 bcopy(lim->pl_rlimit, newlim->pl_rlimit, 515 sizeof(struct rlimit) * RLIM_NLIMITS); 516 newlim->p_refcnt = 1; 517 return (newlim); 518} 519 520void 521limfree(struct plimit *lim) 522{ 523 if (--lim->p_refcnt > 0) 524 return; 525 pool_put(&plimit_pool, lim); 526} 527