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

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

Reply via email to