From owner-freebsd-hackers Sun Feb 10 13:34:37 2002 Delivered-To: freebsd-hackers@freebsd.org Received: from mail.web.am (wizard.web.am [217.113.0.66]) by hub.freebsd.org (Postfix) with SMTP id 70FC937B42C for ; Sun, 10 Feb 2002 13:33:39 -0800 (PST) Received: (qmail 66402 invoked from network); 10 Feb 2002 21:46:01 -0000 Received: from unknown (HELO nm.web.am) (217.113.3.18) by wizard.web.am with SMTP; 10 Feb 2002 21:46:01 -0000 Received: (from nm@localhost) by nm.web.am (8.11.6/8.11.4) id g1ALk1921348 for freebsd-hackers@FreeBSD.ORG; Mon, 11 Feb 2002 01:46:01 +0400 (AMT) (envelope-from nm) Date: Mon, 11 Feb 2002 01:46:01 +0400 From: Gaspar Chilingarov To: freebsd-hackers@FreeBSD.ORG Subject: Re: fork rate limit Message-ID: <20020210214601.GA21111@mail.web.am> References: <20020202201551.GA89061@mail.web.am> Mime-Version: 1.0 Content-Type: multipart/mixed; boundary="qDbXVdCdHGoSgWSk" Content-Disposition: inline In-Reply-To: <20020202201551.GA89061@mail.web.am> User-Agent: Mutt/1.3.25i Sender: owner-freebsd-hackers@FreeBSD.ORG Precedence: bulk List-ID: List-Archive: (Web Archive) List-Help: (List Instructions) List-Subscribe: List-Unsubscribe: X-Loop: FreeBSD.ORG --qDbXVdCdHGoSgWSk Content-Type: text/plain; charset=us-ascii Content-Disposition: inline Hi there! I've implemented suggested limits, and if you are interested, you can try attached patch, if it's OK, i will submit it to PR database. Two new RLIMIT_ constants added, that control how many processes spawn in which period. The only place from where you can set limits is a login.conf. Also fork rate-limit does not affect processes which belong to root. Patch is created against 10 Feb 20:00 UTC sources. copy it to /usr/src and run 'patch -p1 < diffi' -- Gaspar Chilingarov --qDbXVdCdHGoSgWSk Content-Type: text/plain; charset=us-ascii Content-Disposition: attachment; filename=diffi diff -r -u /usr/src/lib/libc/sys/getrlimit.2 src/lib/libc/sys/getrlimit.2 --- /usr/src/lib/libc/sys/getrlimit.2 Mon Oct 1 21:09:01 2001 +++ src/lib/libc/sys/getrlimit.2 Mon Feb 11 00:32:16 2002 @@ -98,6 +98,13 @@ The maximum size (in bytes) of socket buffer usage for this user. This limits the amount of network memory, and hence the amount of mbufs, that this user may hold at any time. +.It Li RLIMIT_FORKPROC +The maximum count of processes that user can start in RLIMIT_PERIOD seconds. +This limit does not apply to superuser. Zero value is not allowed. +Only rlim_max (hard limit) value is used, when applying limits. +.It Li RLIMIT_FORKPERIOD +Amount of seconds, to which applies RLIMIT_FORKPROC. This value cannot be changed by non-superuser. Zero value is not allowed. +Only rlim_max (hard limit) value is used, when applying limits. .El .Pp A resource limit is specified as a soft limit and a hard limit. When a diff -r -u /usr/src/lib/libutil/login.conf.5 src/lib/libutil/login.conf.5 --- /usr/src/lib/libutil/login.conf.5 Fri Nov 16 08:39:43 2001 +++ src/lib/libutil/login.conf.5 Mon Feb 11 00:31:01 2002 @@ -164,6 +164,8 @@ .It "openfiles number Maximum number of open files per process. .It "sbsize size Maximum permitted socketbuffer size. .It "stacksize size Maximum stack size limit. +.It "forkproc number Maximum number of process allowed to start in 'forkperiod' seconds. +.It "forkperiod number .El .Pp These resource limit entries actually specify both the maximum diff -r -u /usr/src/lib/libutil/login_class.c src/lib/libutil/login_class.c --- /usr/src/lib/libutil/login_class.c Mon Oct 1 03:35:07 2001 +++ src/lib/libutil/login_class.c Mon Feb 11 00:31:01 2002 @@ -47,16 +47,18 @@ rlim_t (*who)(login_cap_t *, const char *, rlim_t, rlim_t); int why; } resources[] = { - { "cputime", login_getcaptime, RLIMIT_CPU }, - { "filesize", login_getcapsize, RLIMIT_FSIZE }, - { "datasize", login_getcapsize, RLIMIT_DATA }, - { "stacksize", login_getcapsize, RLIMIT_STACK }, - { "memoryuse", login_getcapsize, RLIMIT_RSS }, - { "memorylocked", login_getcapsize, RLIMIT_MEMLOCK }, - { "maxproc", login_getcapnum, RLIMIT_NPROC }, - { "openfiles", login_getcapnum, RLIMIT_NOFILE }, - { "coredumpsize", login_getcapsize, RLIMIT_CORE }, - { "sbsize", login_getcapsize, RLIMIT_SBSIZE }, + { "cputime", login_getcaptime, RLIMIT_CPU }, + { "filesize", login_getcapsize, RLIMIT_FSIZE }, + { "datasize", login_getcapsize, RLIMIT_DATA }, + { "stacksize", login_getcapsize, RLIMIT_STACK }, + { "memoryuse", login_getcapsize, RLIMIT_RSS }, + { "memorylocked", login_getcapsize, RLIMIT_MEMLOCK }, + { "maxproc", login_getcapnum, RLIMIT_NPROC }, + { "openfiles", login_getcapnum, RLIMIT_NOFILE }, + { "coredumpsize", login_getcapsize, RLIMIT_CORE }, + { "sbsize", login_getcapsize, RLIMIT_SBSIZE }, + { "forkproc", login_getcapnum, RLIMIT_FORKPROC }, + { "forkperiod", login_getcapnum, RLIMIT_FORKPERIOD }, { NULL, 0, 0 } }; diff -r -u /usr/src/sys/kern/kern_fork.c src/sys/kern/kern_fork.c --- /usr/src/sys/kern/kern_fork.c Fri Feb 8 03:06:26 2002 +++ src/sys/kern/kern_fork.c Mon Feb 11 00:27:18 2002 @@ -36,7 +36,7 @@ * SUCH DAMAGE. * * @(#)kern_fork.c 8.6 (Berkeley) 4/8/94 - * $FreeBSD: src/sys/kern/kern_fork.c,v 1.130 2002/02/07 23:06:26 peter Exp $ + * $FreeBSD: src/sys/kern/kern_fork.c,v 1.128 2002/01/13 11:57:59 alfred Exp $ */ #include "opt_ktrace.h" @@ -239,9 +239,6 @@ struct forklist *ep; struct filedesc *fd; struct proc *p1 = td->td_proc; - struct thread *td2; - struct kse *ke2; - struct ksegrp *kg2; GIANT_REQUIRED; @@ -249,12 +246,25 @@ if ((flags & (RFFDG|RFCFDG)) == (RFFDG|RFCFDG)) return (EINVAL); + /* never check fork rate limit for superuser */ + uid = p1->p_ucred->cr_ruid; + if (uid != 0) { + ok = chgforkcnt(p1); + if (!ok) { + PROC_LOCK(p1); + killproc(p1, "exceeded maximum fork rate limit"); + PROC_UNLOCK(p1); + return (EAGAIN); /* meaningless? we have killed + calling process */ + } + } + /* * Here we don't create a new process, but we divorce * certain parts of a process from itself. */ if ((flags & RFPROC) == 0) { vm_forkproc(td, NULL, NULL, flags); /* * Close all file descriptors. diff -r -u /usr/src/sys/kern/kern_resource.c src/sys/kern/kern_resource.c --- /usr/src/sys/kern/kern_resource.c Mon Jan 21 02:48:49 2002 +++ src/sys/kern/kern_resource.c Mon Feb 11 00:25:55 2002 @@ -572,6 +572,18 @@ if (limp->rlim_max < 1) limp->rlim_max = 1; break; + case RLIMIT_FORKPERIOD: + /* only superuser allowed to change fork period */ + if (limp->rlim_max != alimp->rlim_max) + if ((error = suser_xxx(0, p, PRISON_ROOT))) + return (error); + /* FALLTHROUGH */ + case RLIMIT_FORKPROC: + /* fork rate cannot be set to 0 + * it will cause KASSERT in chgforkcnt, if set to 0 */ + if (limp->rlim_max == 0) + return (EINVAL); + break; } *alimp = *limp; return (0); @@ -994,4 +1006,49 @@ splx(s); UIDINFO_UNLOCK(uip); return (1); +} + + +/* + * Checks, if user have reached his fork-rate limit, if so - returns 0 + * + * If last user's limit is in past, user can now do max proc_count + */ +int +chgforkcnt(proc) + register struct proc *proc; +{ + struct timeval tv; + register struct uidinfo *uip; + register rlim_t period, proc_count; + + uip = proc->p_ucred->cr_ruidinfo; + period = proc->p_rlimit[RLIMIT_FORKPERIOD].rlim_max; + proc_count = proc->p_rlimit[RLIMIT_FORKPROC].rlim_max; + + if (period == RLIM_INFINITY || proc_count == RLIM_INFINITY) + return 1; + + /* XXX do we really need to allow user to fork, when limit set to 0 ? + * this must never happen! all checks are done in dosetrlimit */ + KASSERT(period != 0 && proc_count != 0, ("chgforkcnt: you hit bug! fork limits must never be zero")); + + getmicrotime(&tv); + UIDINFO_LOCK(uip); + if (uip->fork_allowed <= tv.tv_sec) { + /* we have end of period passed, just set initial + * values and allow forking */ + uip->fork_allowed = tv.tv_sec + period; + uip->fork_remaining = proc_count; + UIDINFO_UNLOCK(uip); + return (1); + } + + if (uip->fork_remaining <= 0) { + UIDINFO_UNLOCK(uip); + return (0); /* disable forking */ + } + uip->fork_remaining--; + UIDINFO_UNLOCK(uip); + return (1); } diff -r -u /usr/src/sys/sys/resource.h src/sys/sys/resource.h --- /usr/src/sys/sys/resource.h Wed Sep 12 13:38:05 2001 +++ src/sys/sys/resource.h Mon Feb 11 00:30:06 2002 @@ -91,8 +91,10 @@ #define RLIMIT_NPROC 7 /* number of processes */ #define RLIMIT_NOFILE 8 /* number of open files */ #define RLIMIT_SBSIZE 9 /* maximum size of all socket buffers */ +#define RLIMIT_FORKPERIOD 10 /* fork rate limits -- period */ +#define RLIMIT_FORKPROC 11 /* process count */ -#define RLIM_NLIMITS 10 /* number of resource limits */ +#define RLIM_NLIMITS 12 /* number of resource limits */ #define RLIM_INFINITY ((rlim_t)(((u_quad_t)1 << 63) - 1)) @@ -113,6 +115,8 @@ "nproc", "nofile", "sbsize", + "forkproc", + "forkperiod", }; #endif diff -r -u /usr/src/sys/sys/resourcevar.h src/sys/sys/resourcevar.h --- /usr/src/sys/sys/resourcevar.h Mon Jan 21 02:48:49 2002 +++ src/sys/sys/resourcevar.h Mon Feb 11 00:30:06 2002 @@ -96,6 +96,9 @@ long ui_proccnt; /* number of processes */ uid_t ui_uid; /* uid */ u_short ui_ref; /* reference count */ + long fork_allowed; /* when to reset fork_remaining counter */ + u_short fork_remaining; /* NM: number of forks remainding in current + * period */ struct mtx *ui_mtxp; /* protect all counts/limits */ }; @@ -113,6 +116,7 @@ int chgproccnt __P((struct uidinfo *uip, int diff, int max)); int chgsbsize __P((struct uidinfo *uip, u_long *hiwat, u_long to, rlim_t max)); +int chgforkcnt __P((struct proc *proc)); int fuswintr __P((void *base)); struct plimit *limcopy __P((struct plimit *lim)); --qDbXVdCdHGoSgWSk-- To Unsubscribe: send mail to majordomo@FreeBSD.org with "unsubscribe freebsd-hackers" in the body of the message