diff options
author | stevel@tonic-gate <none@none> | 2005-06-14 00:00:00 -0700 |
---|---|---|
committer | stevel@tonic-gate <none@none> | 2005-06-14 00:00:00 -0700 |
commit | 7c478bd95313f5f23a4c958a745db2134aa03244 (patch) | |
tree | c871e58545497667cbb4b0a4f2daf204743e1fe7 /usr/src/uts/common/syscall/rlimit.c | |
download | illumos-joyent-7c478bd95313f5f23a4c958a745db2134aa03244.tar.gz |
OpenSolaris Launch
Diffstat (limited to 'usr/src/uts/common/syscall/rlimit.c')
-rw-r--r-- | usr/src/uts/common/syscall/rlimit.c | 487 |
1 files changed, 487 insertions, 0 deletions
diff --git a/usr/src/uts/common/syscall/rlimit.c b/usr/src/uts/common/syscall/rlimit.c new file mode 100644 index 0000000000..eac3584764 --- /dev/null +++ b/usr/src/uts/common/syscall/rlimit.c @@ -0,0 +1,487 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (the "License"). You may not use this file except in compliance + * with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2004 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ +/* All Rights Reserved */ + + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <sys/param.h> +#include <sys/types.h> +#include <sys/inttypes.h> +#include <sys/sysmacros.h> +#include <sys/systm.h> +#include <sys/tuneable.h> +#include <sys/user.h> +#include <sys/errno.h> +#include <sys/vnode.h> +#include <sys/file.h> +#include <sys/proc.h> +#include <sys/resource.h> +#include <sys/ulimit.h> +#include <sys/debug.h> +#include <sys/rctl.h> + +#include <vm/as.h> + +/* + * Perhaps ulimit could be moved into a user library, as calls to + * getrlimit and setrlimit, were it not for binary compatibility + * restrictions. + */ +long +ulimit(int cmd, long arg) +{ + proc_t *p = curproc; + long retval; + + switch (cmd) { + + case UL_GFILLIM: /* Return current file size limit. */ + { + rlim64_t filesize; + + mutex_enter(&p->p_lock); + filesize = rctl_enforced_value(rctlproc_legacy[RLIMIT_FSIZE], + p->p_rctls, p); + mutex_exit(&p->p_lock); + + if (get_udatamodel() == DATAMODEL_ILP32) { + /* + * File size is returned in blocks for ulimit. + * This function is deprecated and therefore LFS API + * didn't define the behaviour of ulimit. + * Here we return maximum value of file size possible + * so that applications that do not check errors + * continue to work. + */ + if (filesize > MAXOFF32_T) + filesize = MAXOFF32_T; + retval = ((int)filesize >> SCTRSHFT); + } else + retval = filesize >> SCTRSHFT; + break; + } + + case UL_SFILLIM: /* Set new file size limit. */ + { + int error = 0; + rlim64_t lim = (rlim64_t)arg; + struct rlimit64 rl64; + rctl_alloc_gp_t *gp = rctl_rlimit_set_prealloc(1); + + if (lim >= (((rlim64_t)MAXOFFSET_T) >> SCTRSHFT)) + lim = (rlim64_t)RLIM64_INFINITY; + else + lim <<= SCTRSHFT; + + rl64.rlim_max = rl64.rlim_cur = lim; + mutex_enter(&p->p_lock); + if (error = rctl_rlimit_set(rctlproc_legacy[RLIMIT_FSIZE], p, + &rl64, gp, RCTL_LOCAL_DENY | RCTL_LOCAL_SIGNAL, SIGXFSZ, + CRED())) { + mutex_exit(&p->p_lock); + rctl_prealloc_destroy(gp); + return (set_errno(error)); + } + mutex_exit(&p->p_lock); + rctl_prealloc_destroy(gp); + retval = arg; + break; + } + + case UL_GMEMLIM: /* Return maximum possible break value. */ + { + struct seg *seg; + struct seg *nextseg; + struct as *as = p->p_as; + caddr_t brkend; + caddr_t brkbase; + size_t size; + rlim64_t size_ctl; + rlim64_t vmem_ctl; + + /* + * Find the segment with a virtual address + * greater than the end of the current break. + */ + nextseg = NULL; + mutex_enter(&p->p_lock); + brkbase = (caddr_t)p->p_brkbase; + brkend = (caddr_t)p->p_brkbase + p->p_brksize; + mutex_exit(&p->p_lock); + + /* + * Since we can't return less than the current break, + * initialize the return value to the current break + */ + retval = (long)brkend; + + AS_LOCK_ENTER(as, &as->a_lock, RW_READER); + for (seg = as_findseg(as, brkend, 0); seg != NULL; + seg = AS_SEGNEXT(as, seg)) { + if (seg->s_base >= brkend) { + nextseg = seg; + break; + } + } + + mutex_enter(&p->p_lock); + size_ctl = rctl_enforced_value(rctlproc_legacy[RLIMIT_DATA], + p->p_rctls, p); + vmem_ctl = rctl_enforced_value(rctlproc_legacy[RLIMIT_VMEM], + p->p_rctls, p); + mutex_exit(&p->p_lock); + + /* + * First, calculate the maximum break value based on + * the user's RLIMIT_DATA, but also taking into account + * that this value cannot be greater than as->a_userlimit. + * We also take care to make sure that we don't overflow + * in the calculation. + */ + /* + * Since we are casting the RLIMIT_DATA value to a + * ulong (a 32-bit value in the 32-bit kernel) we have + * to pass this assertion. + */ + ASSERT32((size_t)size_ctl <= UINT32_MAX); + + size = (size_t)size_ctl; + if (as->a_userlimit - brkbase > size) + retval = MAX((size_t)retval, (size_t)(brkbase + size)); + /* don't return less than current */ + else + retval = (long)as->a_userlimit; + + /* + * The max break cannot extend into the next segment + */ + if (nextseg != NULL) + retval = MIN((uintptr_t)retval, + (uintptr_t)nextseg->s_base); + + /* + * Handle the case where there is an limit on RLIMIT_VMEM + */ + if (vmem_ctl < UINT64_MAX) { + /* calculate brkend based on the end of page */ + caddr_t brkendpg = (caddr_t)roundup((uintptr_t)brkend, + PAGESIZE); + /* + * Large Files: The following assertion has to pass + * through to ensure the correctness of the cast. + */ + ASSERT32(vmem_ctl <= UINT32_MAX); + + size = (size_t)(vmem_ctl & PAGEMASK); + + if (as->a_size < size) + size -= as->a_size; + else + size = 0; + /* + * Take care to not overflow the calculation + */ + if (as->a_userlimit - brkendpg > size) + retval = MIN((size_t)retval, + (size_t)(brkendpg + size)); + } + + AS_LOCK_EXIT(as, &as->a_lock); + + /* truncate to same boundary as sbrk */ + + switch (get_udatamodel()) { + default: + case DATAMODEL_ILP32: + retval = retval & ~(8-1); + break; + case DATAMODEL_LP64: + retval = retval & ~(16-1); + break; + } + break; + } + + case UL_GDESLIM: /* Return approximate number of open files */ + { + rlim64_t fdno_ctl; + + mutex_enter(&curproc->p_lock); + fdno_ctl = rctl_enforced_value(rctlproc_legacy[RLIMIT_NOFILE], + curproc->p_rctls, curproc); + ASSERT(fdno_ctl <= INT_MAX); + retval = (rlim_t)fdno_ctl; + mutex_exit(&curproc->p_lock); + break; + } + + default: + return (set_errno(EINVAL)); + + } + return (retval); +} + +#ifdef _SYSCALL32_IMPL + +int +ulimit32(int cmd, int arg) +{ + return ((int)ulimit(cmd, (long)arg)); +} + +#endif /* _SYSCALL32_IMPL */ + +#if defined(_ILP32) || defined(_SYSCALL32_IMPL) + +/* + * Large Files: getrlimit returns RLIM_SAVED_CUR or RLIM_SAVED_MAX when + * rlim_cur or rlim_max is not representable in 32-bit rlim_t. These + * values are just tokens which will be used in setrlimit to set the + * correct limits. The current limits are saved in the saved_rlimit members + * in user structures when the token is returned. setrlimit restores + * the limit values to these saved values when the token is passed. + * Consider the following common scenario of the apps: + * + * limit = getrlimit(); + * savedlimit = limit; + * limit = limit1; + * setrlimit(limit) + * // execute all processes in the new rlimit state. + * setrlimit(savedlimit) // restore the old values. + * + * Most apps don't check error returns from getrlimit or setrlimit + * and this is why we return tokens when the correct value + * cannot be represented in rlim_t. For more discussion refer to + * the LFS API document. + * + * In the 64-bit kernel, all existing resource limits are treated in this + * manner. In the 32-bit kernel, CPU time is treated equivalently to the + * file size limit above; the VM-related limits are not. The macro, + * RLIM_SAVED(x), returns true if the resource limit should be handled in + * this way on the current kernel. + */ +int +getrlimit32(int resource, struct rlimit32 *rlp) +{ + struct rlimit32 rlim32; + struct rlimit64 rlim64; + struct proc *p = curproc; + struct user *up = PTOU(p); + int savecur = 0; + int savemax = 0; + + if (resource < 0 || resource >= RLIM_NLIMITS) + return (set_errno(EINVAL)); + + mutex_enter(&p->p_lock); + (void) rctl_rlimit_get(rctlproc_legacy[resource], p, &rlim64); + mutex_exit(&p->p_lock); + + if (rlim64.rlim_max > (rlim64_t)UINT32_MAX) { + + if (rlim64.rlim_max == RLIM64_INFINITY) + rlim32.rlim_max = RLIM32_INFINITY; + else { + savemax = 1; + rlim32.rlim_max = RLIM32_SAVED_MAX; + /*CONSTCOND*/ + ASSERT(RLIM_SAVED(resource)); + } + + if (rlim64.rlim_cur == RLIM64_INFINITY) + rlim32.rlim_cur = RLIM32_INFINITY; + else if (rlim64.rlim_cur == rlim64.rlim_max) { + savecur = 1; + rlim32.rlim_cur = RLIM32_SAVED_MAX; + /*CONSTCOND*/ + ASSERT(RLIM_SAVED(resource)); + } else if (rlim64.rlim_cur > (rlim64_t)UINT32_MAX) { + savecur = 1; + rlim32.rlim_cur = RLIM32_SAVED_CUR; + /*CONSTCOND*/ + ASSERT(RLIM_SAVED(resource)); + } else + rlim32.rlim_cur = rlim64.rlim_cur; + + /* + * save the current limits in user structure. + */ + /*CONSTCOND*/ + if (RLIM_SAVED(resource)) { + mutex_enter(&p->p_lock); + if (savemax) + up->u_saved_rlimit[resource].rlim_max = + rlim64.rlim_max; + if (savecur) + up->u_saved_rlimit[resource].rlim_cur = + rlim64.rlim_cur; + mutex_exit(&p->p_lock); + } + } else { + ASSERT(rlim64.rlim_cur <= (rlim64_t)UINT32_MAX); + rlim32.rlim_max = rlim64.rlim_max; + rlim32.rlim_cur = rlim64.rlim_cur; + } + + if (copyout(&rlim32, rlp, sizeof (rlim32))) + return (set_errno(EFAULT)); + + return (0); +} + +/* + * See comments above getrlimit32(). When the tokens are passed in the + * rlimit structure the values are considered equal to the values + * stored in saved_rlimit members of user structure. + * When the user passes RLIM_INFINITY to set the resource limit to + * unlimited internally understand this value as RLIM64_INFINITY and + * let rlimit() do the job. + */ +int +setrlimit32(int resource, struct rlimit32 *rlp) +{ + struct rlimit32 rlim32; + struct rlimit64 rlim64; + struct rlimit64 saved_rlim; + int error; + struct proc *p = ttoproc(curthread); + struct user *up = PTOU(p); + rctl_alloc_gp_t *gp; + + if (resource < 0 || resource >= RLIM_NLIMITS) + return (set_errno(EINVAL)); + if (copyin(rlp, &rlim32, sizeof (rlim32))) + return (set_errno(EFAULT)); + + gp = rctl_rlimit_set_prealloc(1); + + /* + * Disallow resource limit tunnelling + */ + /*CONSTCOND*/ + if (RLIM_SAVED(resource)) { + mutex_enter(&p->p_lock); + saved_rlim = up->u_saved_rlimit[resource]; + mutex_exit(&p->p_lock); + } else { + saved_rlim.rlim_max = (rlim64_t)rlim32.rlim_max; + saved_rlim.rlim_cur = (rlim64_t)rlim32.rlim_cur; + } + + switch (rlim32.rlim_cur) { + case RLIM32_INFINITY: + rlim64.rlim_cur = RLIM64_INFINITY; + break; + case RLIM32_SAVED_CUR: + rlim64.rlim_cur = saved_rlim.rlim_cur; + break; + case RLIM32_SAVED_MAX: + rlim64.rlim_cur = saved_rlim.rlim_max; + break; + default: + rlim64.rlim_cur = (rlim64_t)rlim32.rlim_cur; + break; + } + + switch (rlim32.rlim_max) { + case RLIM32_INFINITY: + rlim64.rlim_max = RLIM64_INFINITY; + break; + case RLIM32_SAVED_MAX: + rlim64.rlim_max = saved_rlim.rlim_max; + break; + case RLIM32_SAVED_CUR: + rlim64.rlim_max = saved_rlim.rlim_cur; + break; + default: + rlim64.rlim_max = (rlim64_t)rlim32.rlim_max; + break; + } + + mutex_enter(&p->p_lock); + if (error = rctl_rlimit_set(rctlproc_legacy[resource], p, &rlim64, gp, + rctlproc_flags[resource], rctlproc_signals[resource], CRED())) { + mutex_exit(&p->p_lock); + rctl_prealloc_destroy(gp); + return (set_errno(error)); + } + mutex_exit(&p->p_lock); + rctl_prealloc_destroy(gp); + + return (0); +} + +#endif /* _ILP32 && _SYSCALL32_IMPL */ + +int +getrlimit64(int resource, struct rlimit64 *rlp) +{ + struct rlimit64 rlim64; + struct proc *p = ttoproc(curthread); + + if (resource < 0 || resource >= RLIM_NLIMITS) + return (set_errno(EINVAL)); + + mutex_enter(&p->p_lock); + (void) rctl_rlimit_get(rctlproc_legacy[resource], p, &rlim64); + mutex_exit(&p->p_lock); + + if (copyout(&rlim64, rlp, sizeof (rlim64))) + return (set_errno(EFAULT)); + return (0); +} + +int +setrlimit64(int resource, struct rlimit64 *rlp) +{ + struct rlimit64 rlim64; + struct proc *p = ttoproc(curthread); + int error; + rctl_alloc_gp_t *gp; + + if (resource < 0 || resource >= RLIM_NLIMITS) + return (set_errno(EINVAL)); + if (copyin(rlp, &rlim64, sizeof (rlim64))) + return (set_errno(EFAULT)); + + gp = rctl_rlimit_set_prealloc(1); + + mutex_enter(&p->p_lock); + if (error = rctl_rlimit_set(rctlproc_legacy[resource], p, &rlim64, gp, + rctlproc_flags[resource], rctlproc_signals[resource], CRED())) { + mutex_exit(&p->p_lock); + rctl_prealloc_destroy(gp); + return (set_errno(error)); + } + mutex_exit(&p->p_lock); + rctl_prealloc_destroy(gp); + return (0); + +} |