diff options
-rw-r--r-- | share/man/man9/Makefile | 4 | ||||
-rw-r--r-- | share/man/man9/lim_cur.9 | 119 | ||||
-rw-r--r-- | share/man/man9/uvm.9 | 8 | ||||
-rw-r--r-- | sys/kern/exec_subr.c | 4 | ||||
-rw-r--r-- | sys/kern/init_main.c | 4 | ||||
-rw-r--r-- | sys/kern/kern_descrip.c | 8 | ||||
-rw-r--r-- | sys/kern/kern_exec.c | 4 | ||||
-rw-r--r-- | sys/kern/kern_exit.c | 13 | ||||
-rw-r--r-- | sys/kern/kern_fork.c | 11 | ||||
-rw-r--r-- | sys/kern/kern_resource.c | 190 | ||||
-rw-r--r-- | sys/kern/kern_sig.c | 5 | ||||
-rw-r--r-- | sys/kern/sys_generic.c | 4 | ||||
-rw-r--r-- | sys/kern/vfs_vnops.c | 4 | ||||
-rw-r--r-- | sys/sys/proc.h | 13 | ||||
-rw-r--r-- | sys/sys/resourcevar.h | 39 | ||||
-rw-r--r-- | sys/sys/sysctl.h | 12 | ||||
-rw-r--r-- | sys/uvm/uvm_extern.h | 6 | ||||
-rw-r--r-- | sys/uvm/uvm_glue.c | 15 | ||||
-rw-r--r-- | sys/uvm/uvm_mmap.c | 26 | ||||
-rw-r--r-- | sys/uvm/uvm_unix.c | 6 |
20 files changed, 409 insertions, 86 deletions
diff --git a/share/man/man9/Makefile b/share/man/man9/Makefile index 8829966881c..e92d1f3e2e1 100644 --- a/share/man/man9/Makefile +++ b/share/man/man9/Makefile @@ -1,4 +1,4 @@ -# $OpenBSD: Makefile,v 1.294 2019/06/03 01:27:30 cheloha Exp $ +# $OpenBSD: Makefile,v 1.295 2019/06/21 09:39:48 visa Exp $ # $NetBSD: Makefile,v 1.4 1996/01/09 03:23:01 thorpej Exp $ # Makefile for section 9 (kernel function and variable) manual pages. @@ -21,7 +21,7 @@ MAN= aml_evalnode.9 atomic_add_int.9 atomic_cas_uint.9 \ ieee80211_radiotap.9 if_get.9 if_rxr_init.9 ifq_enqueue.9 \ ifq_deq_begin.9 imax.9 iic.9 intro.9 inittodr.9 intr_barrier.9 \ KASSERT.9 km_alloc.9 knote.9 kthread.9 ktrace.9 \ - loadfirmware.9 log.9 \ + lim_cur.9 loadfirmware.9 log.9 \ malloc.9 membar_sync.9 memcmp.9 mbuf.9 mbuf_tags.9 md5.9 mi_switch.9 \ microtime.9 ml_init.9 mq_init.9 mutex.9 \ namei.9 \ diff --git a/share/man/man9/lim_cur.9 b/share/man/man9/lim_cur.9 new file mode 100644 index 00000000000..c05133f760e --- /dev/null +++ b/share/man/man9/lim_cur.9 @@ -0,0 +1,119 @@ +.\" $OpenBSD: lim_cur.9,v 1.1 2019/06/21 09:39:48 visa Exp $ +.\" +.\" Copyright (c) 2019 Visa Hankala +.\" +.\" Permission to use, copy, modify, and distribute this software for any +.\" purpose with or without fee is hereby granted, provided that the above +.\" copyright notice and this permission notice appear in all copies. +.\" +.\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +.\" +.Dd $Mdocdate: June 21 2019 $ +.Dt LIM_CUR 9 +.Os +.Sh NAME +.Nm lim_cur , +.Nm lim_cur_proc , +.Nm lim_fork , +.Nm lim_free , +.Nm lim_read_enter , +.Nm lim_read_leave +.Nd Resource limit interface +.Sh SYNOPSIS +.In sys/types.h +.In sys/resourcevar.h +.Ft rlim_t +.Fn "lim_cur" "int which" +.Ft rlim_t +.Fn "lim_cur_proc" "struct proc *p" "int which" +.Ft struct plimit * +.Fn "lim_fork" "struct process *parent" "struct process *child" +.Ft void +.Fn "lim_free" "struct plimit *limit" +.Ft struct plimit * +.Fn "lim_read_enter" +.Ft void +.Fn "lim_read_leave" "struct plimit *limit" +.Sh DESCRIPTION +The resource limit interface provides read access to the resource limits +of the process. +.Pp +.Fn lim_cur +returns the value of limit +.Fa which +of the current process. +The +.Fa which +can take one of the following values: +.Bd -literal +#define RLIMIT_CPU 0 /* cpu time in milliseconds */ +#define RLIMIT_FSIZE 1 /* maximum file size */ +#define RLIMIT_DATA 2 /* data size */ +#define RLIMIT_STACK 3 /* stack size */ +#define RLIMIT_CORE 4 /* core file size */ +#define RLIMIT_RSS 5 /* resident set size */ +#define RLIMIT_MEMLOCK 6 /* locked-in-memory address space */ +#define RLIMIT_NPROC 7 /* number of processes */ +#define RLIMIT_NOFILE 8 /* number of open files */ +.Ed +.Pp +.Fn lim_cur_proc +is like +.Fn lim_cur +but returns the value of limit +.Fa which +of thread +.Fa p . +.Pp +.Fn lim_read_enter +begins read access to the current process' resource limit structure. +.Pp +.Fn lim_read_leave +finishes read access to the resource limit structure. +.Pp +Sections denoted by +.Fn lim_read_enter +and +.Fn lim_read_leave +cannot nest. +It is not allowed to use +.Fn lim_cur +inside the read section +because the function uses +.Fn lim_read_enter +and +.Fn lim_read_leave +internally. +.Pp +.Fn lim_free +releases the reference +.Fa limit . +.Pp +.Fn lim_fork +makes the process +.Fa child +share the resource limits of process +.Fa parent . +.Sh CONTEXT +.Fn lim_cur , +.Fn lim_cur_proc , +.Fn lim_fork , +.Fn lim_free , +.Fn lim_read_enter +and +.Fn lim_read_leave +can be called during autoconf, or from process context. +.Sh RETURN VALUES +.Fn lim_cur +and +.Fn lim_cur_proc +return the value of the given resource limit. +.Pp +.Fn lim_read_enter +returns a read reference to the current process' resource limits. diff --git a/share/man/man9/uvm.9 b/share/man/man9/uvm.9 index af1d5d01b34..7a2dd78c20d 100644 --- a/share/man/man9/uvm.9 +++ b/share/man/man9/uvm.9 @@ -1,4 +1,4 @@ -.\" $OpenBSD: uvm.9,v 1.69 2018/06/19 22:35:07 krw Exp $ +.\" $OpenBSD: uvm.9,v 1.70 2019/06/21 09:39:48 visa Exp $ .\" $NetBSD: uvm.9,v 1.14 2000/06/29 06:08:44 mrg Exp $ .\" .\" Copyright (c) 1998 Matthew R. Green @@ -30,7 +30,7 @@ .\" XXX this manual sets nS to 1 or 0 in the description, to obtain .\" synopsis-like function prototypes. any better way? .\" -.Dd $Mdocdate: June 19 2018 $ +.Dd $Mdocdate: June 21 2019 $ .Dt UVM_INIT 9 .Os .Sh NAME @@ -132,7 +132,7 @@ for the machine dependent layer. .Ft void .Fn uvm_init "void" .Ft void -.Fn uvm_init_limits "struct proc *p" +.Fn uvm_init_limits "struct plimit *limit0" .Ft void .Fn uvm_setpagesize "void" .Ft void @@ -155,7 +155,7 @@ function. .Pp The .Fn uvm_init_limits -function initialises process limits for the named process. +function initialises process limits in the given limit structure. This is for use by the system startup for process zero, before any other processes are created. .Pp diff --git a/sys/kern/exec_subr.c b/sys/kern/exec_subr.c index f2282a4a357..f32be4bddf4 100644 --- a/sys/kern/exec_subr.c +++ b/sys/kern/exec_subr.c @@ -1,4 +1,4 @@ -/* $OpenBSD: exec_subr.c,v 1.55 2018/04/12 17:13:44 deraadt Exp $ */ +/* $OpenBSD: exec_subr.c,v 1.56 2019/06/21 09:39:48 visa Exp $ */ /* $NetBSD: exec_subr.c,v 1.9 1994/12/04 03:10:42 mycroft Exp $ */ /* @@ -351,7 +351,7 @@ exec_setup_stack(struct proc *p, struct exec_package *epp) epp->ep_maxsaddr = USRSTACK - MAXSSIZ - MAXSSIZ_GUARD; epp->ep_minsaddr = USRSTACK; #endif - epp->ep_ssize = round_page(p->p_rlimit[RLIMIT_STACK].rlim_cur); + epp->ep_ssize = round_page(lim_cur(RLIMIT_STACK)); if (stackgap_random != 0) { sgap = arc4random() & (stackgap_random - 1); diff --git a/sys/kern/init_main.c b/sys/kern/init_main.c index b4bacaae1b7..a6a2b6da51e 100644 --- a/sys/kern/init_main.c +++ b/sys/kern/init_main.c @@ -1,4 +1,4 @@ -/* $OpenBSD: init_main.c,v 1.289 2019/06/20 14:55:22 anton Exp $ */ +/* $OpenBSD: init_main.c,v 1.290 2019/06/21 09:39:48 visa Exp $ */ /* $NetBSD: init_main.c,v 1.84.4.1 1996/06/02 09:08:06 mrg Exp $ */ /* @@ -373,7 +373,7 @@ main(void *framep) cpu_configure(); /* Configure virtual memory system, set vm rlimits. */ - uvm_init_limits(p); + uvm_init_limits(&limit0); /* Per CPU memory allocation */ percpu_init(); diff --git a/sys/kern/kern_descrip.c b/sys/kern/kern_descrip.c index 7da5dadb4ee..45fae009691 100644 --- a/sys/kern/kern_descrip.c +++ b/sys/kern/kern_descrip.c @@ -1,4 +1,4 @@ -/* $OpenBSD: kern_descrip.c,v 1.184 2019/05/13 17:31:51 deraadt Exp $ */ +/* $OpenBSD: kern_descrip.c,v 1.185 2019/06/21 09:39:48 visa Exp $ */ /* $NetBSD: kern_descrip.c,v 1.42 1996/03/30 22:24:38 christos Exp $ */ /* @@ -353,7 +353,7 @@ restart: FRELE(fp, p); return (0); } - if ((u_int)new >= p->p_rlimit[RLIMIT_NOFILE].rlim_cur || + if ((u_int)new >= lim_cur(RLIMIT_NOFILE) || (u_int)new >= maxfiles) { FRELE(fp, p); return (EBADF); @@ -414,7 +414,7 @@ restart: case F_DUPFD: case F_DUPFD_CLOEXEC: newmin = (long)SCARG(uap, arg); - if ((u_int)newmin >= p->p_rlimit[RLIMIT_NOFILE].rlim_cur || + if ((u_int)newmin >= lim_cur(RLIMIT_NOFILE) || (u_int)newmin >= maxfiles) { error = EINVAL; break; @@ -864,7 +864,7 @@ fdalloc(struct proc *p, int want, int *result) * expanding the ofile array. */ restart: - lim = min((int)p->p_rlimit[RLIMIT_NOFILE].rlim_cur, maxfiles); + lim = min((int)lim_cur(RLIMIT_NOFILE), maxfiles); last = min(fdp->fd_nfiles, lim); if ((i = want) < fdp->fd_freefile) i = fdp->fd_freefile; diff --git a/sys/kern/kern_exec.c b/sys/kern/kern_exec.c index 3019366cb1c..301d847f1c8 100644 --- a/sys/kern/kern_exec.c +++ b/sys/kern/kern_exec.c @@ -1,4 +1,4 @@ -/* $OpenBSD: kern_exec.c,v 1.205 2019/06/01 14:11:17 mpi Exp $ */ +/* $OpenBSD: kern_exec.c,v 1.206 2019/06/21 09:39:48 visa Exp $ */ /* $NetBSD: kern_exec.c,v 1.75 1996/02/09 18:59:28 christos Exp $ */ /*- @@ -201,7 +201,7 @@ check_exec(struct proc *p, struct exec_package *epp) /* check limits */ if ((epp->ep_tsize > MAXTSIZ) || - (epp->ep_dsize > p->p_rlimit[RLIMIT_DATA].rlim_cur)) + (epp->ep_dsize > lim_cur(RLIMIT_DATA))) error = ENOMEM; if (!error) diff --git a/sys/kern/kern_exit.c b/sys/kern/kern_exit.c index 74ac47d02f8..ab2f0de086d 100644 --- a/sys/kern/kern_exit.c +++ b/sys/kern/kern_exit.c @@ -1,4 +1,4 @@ -/* $OpenBSD: kern_exit.c,v 1.177 2019/06/13 21:19:28 mpi Exp $ */ +/* $OpenBSD: kern_exit.c,v 1.178 2019/06/21 09:39:48 visa Exp $ */ /* $NetBSD: kern_exit.c,v 1.39 1996/04/22 01:38:25 christos Exp $ */ /* @@ -328,6 +328,15 @@ exit1(struct proc *p, int rv, int flags) KASSERT(pr->ps_refcnt > 0); } + /* Release the thread's read reference of resource limit structure. */ + if (p->p_limit != NULL) { + struct plimit *limit; + + limit = p->p_limit; + p->p_limit = NULL; + lim_free(limit); + } + /* * Other substructures are freed from reaper and wait(). */ @@ -636,7 +645,7 @@ process_zap(struct process *pr) free(pr->ps_ptstat, M_SUBPROC, sizeof(*pr->ps_ptstat)); pool_put(&rusage_pool, pr->ps_ru); KASSERT(TAILQ_EMPTY(&pr->ps_threads)); - limfree(pr->ps_limit); + lim_free(pr->ps_limit); crfree(pr->ps_ucred); pool_put(&process_pool, pr); nprocesses--; diff --git a/sys/kern/kern_fork.c b/sys/kern/kern_fork.c index 190c749720e..95180d9de31 100644 --- a/sys/kern/kern_fork.c +++ b/sys/kern/kern_fork.c @@ -1,4 +1,4 @@ -/* $OpenBSD: kern_fork.c,v 1.212 2019/06/01 14:11:17 mpi Exp $ */ +/* $OpenBSD: kern_fork.c,v 1.213 2019/06/21 09:39:48 visa Exp $ */ /* $NetBSD: kern_fork.c,v 1.29 1996/02/09 18:59:34 christos Exp $ */ /* @@ -151,6 +151,7 @@ thread_new(struct proc *parent, vaddr_t uaddr) p = pool_get(&proc_pool, PR_WAITOK); p->p_stat = SIDL; /* protect against others */ p->p_flag = 0; + p->p_limit = NULL; /* * Make a proc table entry for the new process. @@ -210,6 +211,8 @@ process_initialize(struct process *pr, struct proc *p) LIST_INIT(&pr->ps_kqlist); LIST_INIT(&pr->ps_sigiolst); + mtx_init(&pr->ps_mtx, IPL_MPFLOOR); + timeout_set(&pr->ps_realit_to, realitexpire, pr); timeout_set(&pr->ps_rucheck_to, rucheck, pr); } @@ -237,12 +240,10 @@ process_new(struct proc *p, struct process *parent, int flags) process_initialize(pr, p); pr->ps_pid = allocpid(); + lim_fork(parent, pr); /* post-copy fixups */ pr->ps_pptr = parent; - pr->ps_limit->pl_refcnt++; - if (pr->ps_limit->pl_rlimit[RLIMIT_CPU].rlim_cur != RLIM_INFINITY) - timeout_add_msec(&pr->ps_rucheck_to, RUCHECK_INTERVAL); /* bump references to the text vnode (for sysctl) */ pr->ps_textvp = parent->ps_textvp; @@ -373,7 +374,7 @@ fork1(struct proc *curp, int flags, void (*func)(void *), void *arg, * Don't allow a nonprivileged user to exceed their current limit. */ count = chgproccnt(uid, 1); - if (uid != 0 && count > curp->p_rlimit[RLIMIT_NPROC].rlim_cur) { + if (uid != 0 && count > lim_cur(RLIMIT_NPROC)) { (void)chgproccnt(uid, -1); nprocesses--; nthreads--; diff --git a/sys/kern/kern_resource.c b/sys/kern/kern_resource.c index 3bc9425020a..050326ab6fd 100644 --- a/sys/kern/kern_resource.c +++ b/sys/kern/kern_resource.c @@ -1,4 +1,4 @@ -/* $OpenBSD: kern_resource.c,v 1.64 2019/06/10 03:15:53 visa Exp $ */ +/* $OpenBSD: kern_resource.c,v 1.65 2019/06/21 09:39:48 visa Exp $ */ /* $NetBSD: kern_resource.c,v 1.38 1996/10/23 07:19:38 matthias Exp $ */ /*- @@ -53,9 +53,16 @@ #include <uvm/uvm_extern.h> +/* Resource usage check interval in msec */ +#define RUCHECK_INTERVAL 1000 + /* SIGXCPU interval in seconds of process runtime */ #define SIGXCPU_INTERVAL 5 +struct plimit *lim_copy(struct plimit *); +struct plimit *lim_write_begin(void); +void lim_write_commit(struct plimit *); + void tuagg_sub(struct tusage *, struct proc *); /* @@ -65,6 +72,13 @@ rlim_t maxdmap = MAXDSIZ; rlim_t maxsmap = MAXSSIZ; /* + * Serializes resource limit updates. + * This lock has to be held together with ps_mtx when updating + * the process' ps_limit. + */ +struct rwlock rlimit_lock = RWLOCK_INITIALIZER("rlimitlk"); + +/* * Resource controls and accounting. */ @@ -229,25 +243,27 @@ int dosetrlimit(struct proc *p, u_int which, struct rlimit *limp) { struct rlimit *alimp; + struct plimit *limit; rlim_t maxlim; int error; if (which >= RLIM_NLIMITS || limp->rlim_cur > limp->rlim_max) return (EINVAL); - alimp = &p->p_rlimit[which]; - if (limp->rlim_max > alimp->rlim_max) - if ((error = suser(p)) != 0) - return (error); - if (p->p_p->ps_limit->pl_refcnt > 1) { - struct plimit *l = p->p_p->ps_limit; + rw_enter_write(&rlimit_lock); - /* limcopy() can sleep, so copy before decrementing refcnt */ - p->p_p->ps_limit = limcopy(l); - limfree(l); - alimp = &p->p_rlimit[which]; + alimp = &p->p_p->ps_limit->pl_rlimit[which]; + if (limp->rlim_max > alimp->rlim_max) { + if ((error = suser(p)) != 0) { + rw_exit_write(&rlimit_lock); + return (error); + } } + /* Get exclusive write access to the limit structure. */ + limit = lim_write_begin(); + alimp = &limit->pl_rlimit[which]; + switch (which) { case RLIMIT_DATA: maxlim = maxdmap; @@ -316,6 +332,10 @@ dosetrlimit(struct proc *p, u_int which, struct rlimit *limp) } *alimp = *limp; + + lim_write_commit(limit); + rw_exit_write(&rlimit_lock); + return (0); } @@ -326,16 +346,19 @@ sys_getrlimit(struct proc *p, void *v, register_t *retval) syscallarg(int) which; syscallarg(struct rlimit *) rlp; } */ *uap = v; - struct rlimit *alimp; + struct plimit *limit; + struct rlimit alimp; int error; if (SCARG(uap, which) < 0 || SCARG(uap, which) >= RLIM_NLIMITS) return (EINVAL); - alimp = &p->p_rlimit[SCARG(uap, which)]; - error = copyout(alimp, SCARG(uap, rlp), sizeof(struct rlimit)); + limit = lim_read_enter(); + alimp = limit->pl_rlimit[SCARG(uap, which)]; + lim_read_leave(limit); + error = copyout(&alimp, SCARG(uap, rlp), sizeof(struct rlimit)); #ifdef KTRACE if (error == 0 && KTRPOINT(p, KTR_STRUCT)) - ktrrlimit(p, alimp); + ktrrlimit(p, &alimp); #endif return (error); } @@ -507,8 +530,8 @@ ruadd(struct rusage *ru, struct rusage *ru2) void rucheck(void *arg) { + struct rlimit rlim; struct process *pr = arg; - struct rlimit *rlim; time_t runtime; int s; @@ -518,9 +541,12 @@ rucheck(void *arg) runtime = pr->ps_tu.tu_runtime.tv_sec; SCHED_UNLOCK(s); - rlim = &pr->ps_limit->pl_rlimit[RLIMIT_CPU]; - if ((rlim_t)runtime >= rlim->rlim_cur) { - if ((rlim_t)runtime >= rlim->rlim_max) { + mtx_enter(&pr->ps_mtx); + rlim = pr->ps_limit->pl_rlimit[RLIMIT_CPU]; + mtx_leave(&pr->ps_mtx); + + if ((rlim_t)runtime >= rlim.rlim_cur) { + if ((rlim_t)runtime >= rlim.rlim_max) { prsignal(pr, SIGKILL); } else if (runtime >= pr->ps_nextxcpu) { prsignal(pr, SIGXCPU); @@ -562,7 +588,7 @@ lim_startup(struct plimit *limit0) * and copy when a limit is changed. */ struct plimit * -limcopy(struct plimit *lim) +lim_copy(struct plimit *lim) { struct plimit *newlim; @@ -574,9 +600,129 @@ limcopy(struct plimit *lim) } void -limfree(struct plimit *lim) +lim_free(struct plimit *lim) { - if (--lim->pl_refcnt > 0) + if (atomic_dec_int_nv(&lim->pl_refcnt) > 0) return; pool_put(&plimit_pool, lim); } + +void +lim_fork(struct process *parent, struct process *child) +{ + struct plimit *limit; + + mtx_enter(&parent->ps_mtx); + limit = parent->ps_limit; + atomic_inc_int(&limit->pl_refcnt); + mtx_leave(&parent->ps_mtx); + + child->ps_limit = limit; + + if (limit->pl_rlimit[RLIMIT_CPU].rlim_cur != RLIM_INFINITY) + timeout_add_msec(&child->ps_rucheck_to, RUCHECK_INTERVAL); +} + +/* + * Return an exclusive write reference to the process' resource limit structure. + * The caller has to release the structure by calling lim_write_commit(). + * + * This invalidates any plimit read reference held by the calling thread. + */ +struct plimit * +lim_write_begin(void) +{ + struct plimit *limit; + struct proc *p = curproc; + + rw_assert_wrlock(&rlimit_lock); + + if (p->p_limit != NULL) + lim_free(p->p_limit); + p->p_limit = NULL; + + /* + * It is safe to access ps_limit here without holding ps_mtx + * because rlimit_lock excludes other writers. + */ + + limit = p->p_p->ps_limit; + if (P_HASSIBLING(p) || limit->pl_refcnt > 1) + limit = lim_copy(limit); + + return (limit); +} + +/* + * Finish exclusive write access to the plimit structure. + * This makes the structure visible to other threads in the process. + */ +void +lim_write_commit(struct plimit *limit) +{ + struct plimit *olimit; + struct proc *p = curproc; + + rw_assert_wrlock(&rlimit_lock); + + if (limit != p->p_p->ps_limit) { + mtx_enter(&p->p_p->ps_mtx); + olimit = p->p_p->ps_limit; + p->p_p->ps_limit = limit; + mtx_leave(&p->p_p->ps_mtx); + + lim_free(olimit); + } +} + +/* + * Begin read access to the process' resource limit structure. + * The access has to be finished by calling lim_read_leave(). + * + * Sections denoted by lim_read_enter() and lim_read_leave() cannot nest. + */ +struct plimit * +lim_read_enter(void) +{ + struct plimit *limit; + struct proc *p = curproc; + struct process *pr = p->p_p; + + /* + * This thread might not observe the latest value of ps_limit + * if another thread updated the limits very recently on another CPU. + * However, the anomaly should disappear quickly, especially if + * there is any synchronization activity between the threads (or + * the CPUs). + */ + + limit = p->p_limit; + if (limit != pr->ps_limit) { + mtx_enter(&pr->ps_mtx); + limit = pr->ps_limit; + atomic_inc_int(&limit->pl_refcnt); + mtx_leave(&pr->ps_mtx); + if (p->p_limit != NULL) + lim_free(p->p_limit); + p->p_limit = limit; + } + KASSERT(limit != NULL); + return (limit); +} + +/* + * Get the value of the resource limit in given process. + */ +rlim_t +lim_cur_proc(struct proc *p, int which) +{ + struct process *pr = p->p_p; + rlim_t val; + + KASSERT(which >= 0 && which < RLIM_NLIMITS); + + mtx_enter(&pr->ps_mtx); + val = pr->ps_limit->pl_rlimit[which].rlim_cur; + mtx_leave(&pr->ps_mtx); + return (val); +} diff --git a/sys/kern/kern_sig.c b/sys/kern/kern_sig.c index 4e3efa9c736..5738cd71af1 100644 --- a/sys/kern/kern_sig.c +++ b/sys/kern/kern_sig.c @@ -1,4 +1,4 @@ -/* $OpenBSD: kern_sig.c,v 1.230 2019/05/13 19:21:31 bluhm Exp $ */ +/* $OpenBSD: kern_sig.c,v 1.231 2019/06/21 09:39:48 visa Exp $ */ /* $NetBSD: kern_sig.c,v 1.54 1996/04/22 01:38:32 christos Exp $ */ /* @@ -1548,8 +1548,7 @@ coredump(struct proc *p) } /* Don't dump if will exceed file size limit. */ - if (USPACE + ptoa(vm->vm_dsize + vm->vm_ssize) >= - p->p_rlimit[RLIMIT_CORE].rlim_cur) + if (USPACE + ptoa(vm->vm_dsize + vm->vm_ssize) >= lim_cur(RLIMIT_CORE)) return (EFBIG); if (incrash && nosuidcoredump == 3) { diff --git a/sys/kern/sys_generic.c b/sys/kern/sys_generic.c index ccb8502ed21..5fb4cf04258 100644 --- a/sys/kern/sys_generic.c +++ b/sys/kern/sys_generic.c @@ -1,4 +1,4 @@ -/* $OpenBSD: sys_generic.c,v 1.123 2019/01/21 23:41:26 cheloha Exp $ */ +/* $OpenBSD: sys_generic.c,v 1.124 2019/06/21 09:39:48 visa Exp $ */ /* $NetBSD: sys_generic.c,v 1.24 1996/03/29 00:25:32 cgd Exp $ */ /* @@ -935,7 +935,7 @@ doppoll(struct proc *p, struct pollfd *fds, u_int nfds, int timo, ncoll, i, s, error; /* Standards say no more than MAX_OPEN; this is possibly better. */ - if (nfds > min((int)p->p_rlimit[RLIMIT_NOFILE].rlim_cur, maxfiles)) + if (nfds > min((int)lim_cur(RLIMIT_NOFILE), maxfiles)) return (EINVAL); /* optimize for the default case, of a small nfds value */ diff --git a/sys/kern/vfs_vnops.c b/sys/kern/vfs_vnops.c index 2d5d00e5eff..c02f8f0cba1 100644 --- a/sys/kern/vfs_vnops.c +++ b/sys/kern/vfs_vnops.c @@ -1,4 +1,4 @@ -/* $OpenBSD: vfs_vnops.c,v 1.97 2018/08/20 16:00:22 mpi Exp $ */ +/* $OpenBSD: vfs_vnops.c,v 1.98 2019/06/21 09:39:48 visa Exp $ */ /* $NetBSD: vfs_vnops.c,v 1.20 1996/02/04 02:18:41 christos Exp $ */ /* @@ -247,7 +247,7 @@ vn_fsizechk(struct vnode *vp, struct uio *uio, int ioflag, ssize_t *overrun) *overrun = 0; if (vp->v_type == VREG && p != NULL && !(ioflag & IO_NOLIMIT)) { - rlim_t limit = p->p_rlimit[RLIMIT_FSIZE].rlim_cur; + rlim_t limit = lim_cur_proc(p, RLIMIT_FSIZE); /* if already at or over the limit, send the signal and fail */ if (uio->uio_offset >= limit) { diff --git a/sys/sys/proc.h b/sys/sys/proc.h index 76aac28ddef..cae61ce0d0c 100644 --- a/sys/sys/proc.h +++ b/sys/sys/proc.h @@ -1,4 +1,4 @@ -/* $OpenBSD: proc.h,v 1.269 2019/06/10 03:15:53 visa Exp $ */ +/* $OpenBSD: proc.h,v 1.270 2019/06/21 09:39:48 visa Exp $ */ /* $NetBSD: proc.h,v 1.44 1996/04/22 01:23:21 christos Exp $ */ /*- @@ -154,6 +154,12 @@ RBT_HEAD(unvname_rbt, unvname); struct futex; LIST_HEAD(futex_list, futex); struct unveil; + +/* + * Locks used to protect struct members in this file: + * m this process' `ps_mtx' + * r rlimit_lock + */ struct process { /* * ps_mainproc is the original thread in the process. @@ -181,6 +187,7 @@ struct process { struct futex_list ps_ftlist; /* futexes attached to this process */ LIST_HEAD(, kqueue) ps_kqlist; /* kqueues attached to this process */ + struct mutex ps_mtx; /* per-process mutex */ /* The following fields are all zeroed upon creation in process_new. */ #define ps_startzero ps_klist @@ -221,7 +228,7 @@ struct process { /* The following fields are all copied upon creation in process_new. */ #define ps_startcopy ps_limit - struct plimit *ps_limit; /* Process limits. */ + struct plimit *ps_limit; /* [m,r] Process limits. */ struct pgrp *ps_pgrp; /* Pointer to process group. */ struct emul *ps_emul; /* Emulation information */ @@ -325,7 +332,7 @@ struct proc { struct vmspace *p_vmspace; /* copy of p_p->ps_vmspace */ struct p_inentry p_spinentry; struct p_inentry p_pcinentry; -#define p_rlimit p_p->ps_limit->pl_rlimit + struct plimit *p_limit; /* read reference of p_p->ps_limit */ int p_flag; /* P_* flags. */ u_char p_spare; /* unused */ diff --git a/sys/sys/resourcevar.h b/sys/sys/resourcevar.h index 4e4988e7d04..4200758bd04 100644 --- a/sys/sys/resourcevar.h +++ b/sys/sys/resourcevar.h @@ -1,4 +1,4 @@ -/* $OpenBSD: resourcevar.h,v 1.23 2019/06/02 03:58:28 visa Exp $ */ +/* $OpenBSD: resourcevar.h,v 1.24 2019/06/21 09:39:48 visa Exp $ */ /* $NetBSD: resourcevar.h,v 1.12 1995/11/22 23:01:53 cgd Exp $ */ /* @@ -56,6 +56,9 @@ do { \ } while (0) #ifdef _KERNEL + +#include <lib/libkern/libkern.h> /* for KASSERT() */ + void addupc_intr(struct proc *, u_long); void addupc_task(struct proc *, u_long, u_int); void tuagg_unlocked(struct process *, struct proc *); @@ -66,11 +69,39 @@ void calctsru(struct tusage *, struct timespec *, struct timespec *, void calcru(struct tusage *, struct timeval *, struct timeval *, struct timeval *); void lim_startup(struct plimit *); -struct plimit *limcopy(struct plimit *); -void limfree(struct plimit *); +void lim_free(struct plimit *); +void lim_fork(struct process *, struct process *); +struct plimit *lim_read_enter(void); + +/* + * Finish read access to resource limits. + */ +static inline void +lim_read_leave(struct plimit *limit) +{ + /* nothing */ +} + +/* + * Get the value of the resource limit in current process. + */ +static inline rlim_t +lim_cur(int which) +{ + struct plimit *limit; + rlim_t val; + + KASSERT(which >= 0 && which < RLIM_NLIMITS); + + limit = lim_read_enter(); + val = limit->pl_rlimit[which].rlim_cur; + lim_read_leave(limit); + return (val); +} + +rlim_t lim_cur_proc(struct proc *, int); void ruadd(struct rusage *, struct rusage *); void rucheck(void *); -#define RUCHECK_INTERVAL 1000 /* check interval in msec */ #endif #endif /* !_SYS_RESOURCEVAR_H_ */ diff --git a/sys/sys/sysctl.h b/sys/sys/sysctl.h index d75be8c8658..bb8c521f38e 100644 --- a/sys/sys/sysctl.h +++ b/sys/sys/sysctl.h @@ -1,4 +1,4 @@ -/* $OpenBSD: sysctl.h,v 1.188 2019/06/01 14:11:18 mpi Exp $ */ +/* $OpenBSD: sysctl.h,v 1.189 2019/06/21 09:39:48 visa Exp $ */ /* $NetBSD: sysctl.h,v 1.16 1996/04/09 20:55:36 cgd Exp $ */ /* @@ -536,6 +536,14 @@ struct kinfo_vmentry { * p_tpgid, p_tsess, p_vm_rssize, p_u[us]time_{sec,usec}, p_cpuid */ +#if defined(_KERNEL) +#define PR_LOCK(pr) mtx_enter(&(pr)->ps_mtx) +#define PR_UNLOCK(pr) mtx_leave(&(pr)->ps_mtx) +#else +#define PR_LOCK(pr) /* nothing */ +#define PR_UNLOCK(pr) /* nothing */ +#endif + #define PTRTOINT64(_x) ((u_int64_t)(u_long)(_x)) #define FILL_KPROC(kp, copy_str, p, pr, uc, pg, paddr, \ @@ -638,9 +646,11 @@ do { \ (kp)->p_wchan = PTRTOINT64((p)->p_wchan); \ } \ \ + PR_LOCK(pr); \ if (lim) \ (kp)->p_rlim_rss_cur = \ (lim)->pl_rlimit[RLIMIT_RSS].rlim_cur; \ + PR_UNLOCK(pr); \ \ if (((pr)->ps_flags & PS_ZOMBIE) == 0) { \ struct timeval tv; \ diff --git a/sys/uvm/uvm_extern.h b/sys/uvm/uvm_extern.h index 12dc1f7b32f..74982c37f6c 100644 --- a/sys/uvm/uvm_extern.h +++ b/sys/uvm/uvm_extern.h @@ -1,4 +1,4 @@ -/* $OpenBSD: uvm_extern.h,v 1.146 2019/05/09 20:36:44 beck Exp $ */ +/* $OpenBSD: uvm_extern.h,v 1.147 2019/06/21 09:39:48 visa Exp $ */ /* $NetBSD: uvm_extern.h,v 1.57 2001/03/09 01:02:12 chs Exp $ */ /* @@ -259,6 +259,8 @@ extern vaddr_t vm_min_kernel_address; #define vm_resident_count(vm) (pmap_resident_count((vm)->vm_map.pmap)) +struct plimit; + void vmapbuf(struct buf *, vsize_t); void vunmapbuf(struct buf *, vsize_t); struct uvm_object *uao_create(vsize_t, int); @@ -271,7 +273,7 @@ int uvm_fault(vm_map_t, vaddr_t, vm_fault_t, vm_prot_t); vaddr_t uvm_uarea_alloc(void); void uvm_uarea_free(struct proc *); void uvm_exit(struct process *); -void uvm_init_limits(struct proc *); +void uvm_init_limits(struct plimit *); boolean_t uvm_kernacc(caddr_t, size_t, int); int uvm_vslock(struct proc *, caddr_t, size_t, diff --git a/sys/uvm/uvm_glue.c b/sys/uvm/uvm_glue.c index e17500c47d1..36105185591 100644 --- a/sys/uvm/uvm_glue.c +++ b/sys/uvm/uvm_glue.c @@ -1,4 +1,4 @@ -/* $OpenBSD: uvm_glue.c,v 1.74 2019/01/10 20:26:34 kettenis Exp $ */ +/* $OpenBSD: uvm_glue.c,v 1.75 2019/06/21 09:39:49 visa Exp $ */ /* $NetBSD: uvm_glue.c,v 1.44 2001/02/06 19:54:44 eeh Exp $ */ /* @@ -303,20 +303,19 @@ uvm_exit(struct process *pr) * - called for process 0 and then inherited by all others. */ void -uvm_init_limits(struct proc *p) +uvm_init_limits(struct plimit *limit0) { - /* * Set up the initial limits on process VM. Set the maximum * resident set size to be all of (reasonably) available memory. * This causes any single, large process to start random page * replacement once it fills memory. */ - p->p_rlimit[RLIMIT_STACK].rlim_cur = DFLSSIZ; - p->p_rlimit[RLIMIT_STACK].rlim_max = MAXSSIZ; - p->p_rlimit[RLIMIT_DATA].rlim_cur = DFLDSIZ; - p->p_rlimit[RLIMIT_DATA].rlim_max = MAXDSIZ; - p->p_rlimit[RLIMIT_RSS].rlim_cur = ptoa(uvmexp.free); + limit0->pl_rlimit[RLIMIT_STACK].rlim_cur = DFLSSIZ; + limit0->pl_rlimit[RLIMIT_STACK].rlim_max = MAXSSIZ; + limit0->pl_rlimit[RLIMIT_DATA].rlim_cur = DFLDSIZ; + limit0->pl_rlimit[RLIMIT_DATA].rlim_max = MAXDSIZ; + limit0->pl_rlimit[RLIMIT_RSS].rlim_cur = ptoa(uvmexp.free); } #ifdef DEBUG diff --git a/sys/uvm/uvm_mmap.c b/sys/uvm/uvm_mmap.c index 6b35906f142..9f1a6bd9197 100644 --- a/sys/uvm/uvm_mmap.c +++ b/sys/uvm/uvm_mmap.c @@ -1,4 +1,4 @@ -/* $OpenBSD: uvm_mmap.c,v 1.156 2019/05/11 20:02:00 deraadt Exp $ */ +/* $OpenBSD: uvm_mmap.c,v 1.157 2019/06/21 09:39:49 visa Exp $ */ /* $NetBSD: uvm_mmap.c,v 1.49 2001/02/18 21:19:08 chs Exp $ */ /* @@ -218,7 +218,7 @@ sys_mmap(struct proc *p, void *v, register_t *retval) vaddr_t addr; struct vattr va; off_t pos; - vsize_t size, pageoff; + vsize_t limit, pageoff, size; vm_prot_t prot, maxprot; int flags, fd; vaddr_t vm_min_address = VM_MIN_ADDRESS; @@ -383,16 +383,16 @@ sys_mmap(struct proc *p, void *v, register_t *retval) } if ((flags & __MAP_NOFAULT) != 0 || ((flags & MAP_PRIVATE) != 0 && (prot & PROT_WRITE) != 0)) { - if (p->p_rlimit[RLIMIT_DATA].rlim_cur < size || - p->p_rlimit[RLIMIT_DATA].rlim_cur - size < - ptoa(p->p_vmspace->vm_dused)) { + limit = lim_cur(RLIMIT_DATA); + if (limit < size || + limit - size < ptoa(p->p_vmspace->vm_dused)) { error = ENOMEM; goto out; } } KERNEL_LOCK(); - error = uvm_mmapfile(&p->p_vmspace->vm_map, &addr, size, prot, maxprot, - flags, vp, pos, p->p_rlimit[RLIMIT_MEMLOCK].rlim_cur, p); + error = uvm_mmapfile(&p->p_vmspace->vm_map, &addr, size, prot, + maxprot, flags, vp, pos, lim_cur(RLIMIT_MEMLOCK), p); KERNEL_UNLOCK(); } else { /* MAP_ANON case */ if (fd != -1) @@ -404,9 +404,9 @@ is_anon: /* label for SunOS style /dev/zero */ if ((flags & __MAP_NOFAULT) != 0) return EINVAL; - if (p->p_rlimit[RLIMIT_DATA].rlim_cur < size || - p->p_rlimit[RLIMIT_DATA].rlim_cur - size < - ptoa(p->p_vmspace->vm_dused)) { + limit = lim_cur(RLIMIT_DATA); + if (limit < size || + limit - size < ptoa(p->p_vmspace->vm_dused)) { return ENOMEM; } @@ -419,7 +419,7 @@ is_anon: /* label for SunOS style /dev/zero */ maxprot = PROT_MASK; error = uvm_mmapanon(&p->p_vmspace->vm_map, &addr, size, prot, - maxprot, flags, p->p_rlimit[RLIMIT_MEMLOCK].rlim_cur, p); + maxprot, flags, lim_cur(RLIMIT_MEMLOCK), p); } if (error == 0) @@ -729,7 +729,7 @@ sys_mlock(struct proc *p, void *v, register_t *retval) #ifdef pmap_wired_count if (size + ptoa(pmap_wired_count(vm_map_pmap(&p->p_vmspace->vm_map))) > - p->p_rlimit[RLIMIT_MEMLOCK].rlim_cur) + lim_cur(RLIMIT_MEMLOCK)) return (EAGAIN); #else if ((error = suser(p)) != 0) @@ -798,7 +798,7 @@ sys_mlockall(struct proc *p, void *v, register_t *retval) #endif error = uvm_map_pageable_all(&p->p_vmspace->vm_map, flags, - p->p_rlimit[RLIMIT_MEMLOCK].rlim_cur); + lim_cur(RLIMIT_MEMLOCK)); if (error != 0 && error != ENOMEM) return (EAGAIN); return (error); diff --git a/sys/uvm/uvm_unix.c b/sys/uvm/uvm_unix.c index 4618ce03559..c9dfd786fab 100644 --- a/sys/uvm/uvm_unix.c +++ b/sys/uvm/uvm_unix.c @@ -1,4 +1,4 @@ -/* $OpenBSD: uvm_unix.c,v 1.65 2019/03/01 01:46:18 cheloha Exp $ */ +/* $OpenBSD: uvm_unix.c,v 1.66 2019/06/21 09:39:49 visa Exp $ */ /* $NetBSD: uvm_unix.c,v 1.18 2000/09/13 15:00:25 thorpej Exp $ */ /* @@ -72,7 +72,7 @@ sys_obreak(struct proc *p, void *v, register_t *retval) base = (vaddr_t)vm->vm_daddr; new = round_page((vaddr_t)SCARG(uap, nsize)); - if (new < base || (new - base) > p->p_rlimit[RLIMIT_DATA].rlim_cur) + if (new < base || (new - base) > lim_cur(RLIMIT_DATA)) return (ENOMEM); old = round_page(base + ptoa(vm->vm_dsize)); @@ -128,7 +128,7 @@ uvm_grow(struct proc *p, vaddr_t sp) #else si = atop((vaddr_t)vm->vm_minsaddr - sp) - vm->vm_ssize; #endif - if (vm->vm_ssize + si <= atop(p->p_rlimit[RLIMIT_STACK].rlim_cur)) + if (vm->vm_ssize + si <= atop(lim_cur(RLIMIT_STACK))) vm->vm_ssize += si; } |