Skip site navigation (1)Skip section navigation (2)
Date:      Mon, 11 Feb 2002 01:46:01 +0400
From:      Gaspar Chilingarov <nm@web.am>
To:        freebsd-hackers@FreeBSD.ORG
Subject:   Re: fork rate limit
Message-ID:  <20020210214601.GA21111@mail.web.am>
In-Reply-To: <20020202201551.GA89061@mail.web.am>
References:  <20020202201551.GA89061@mail.web.am>

next in thread | previous in thread | raw e-mail | index | archive | help

--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




Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?20020210214601.GA21111>