On Sun, 10 Feb 2002, Mike Silbersack wrote:
> On Mon, 11 Feb 2002, Gaspar Chilingarov wrote: > > > 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. > > I finished looking at the patch, and I'm not impressed by it. It looks > like the patch I'm working on will work more effectively; I'll post or > commit it in a few days. > > Mike "Silby" Silbersack For those interested, here's the forkbomb countermeasure patch I've devised. It does three things: 1. Change the number of procs reserved for root from 1 to 10. Given how maxprocperuid is set in 4.5, this shouldn't matter much, but it's a safe change. 2. Limit the number of procs to an appropriate number. Previously, it was easy to set maxproc overly high by setting a large maxusers value. With this change, proc-related structures will only be able to consume about 1/2 of all system memory. Without this limitation, a high maxusers setting and a forkbomb could easily consume all system memory, leaving virtually no chance for the system to recover. 3. Penalize processes / users that hit their maxproc limit. Whenever a process tries to fork and that user's proc limit is hit, a tsleep of .5 seconds will occur. This change causes 1000 madly fork()ing processes into 1000 nicely sleeping processes if they continue to attempt to fork(). The testing I've done with this patch has proven it to be quite effective in protecting a system against a simple forkbomb, even if maxusers is set to inordinantly high values. Sysadmins who have already imposed low limits on their users will still be helped by change #3, as it ensures that less processor time will be wasted by continuous fork()ing. There are certainly a few remaining issues with more complex resource exhaustion attacks, but that can be dealt with in future patches. If you were interested in the fork rate limit thread, please give this patch a try to see if it helps in the forkbomb-like situations you've seen. Stable users: This won't compile as is for you. Change + tsleep(&forksleep, td->td_kse->ke_priority, "fork", hz / 2); to + tsleep(&forksleep, PUSER, "fork", hz / 2); Thanks, Mike "Silby" Silbersack
diff -u -r /usr/src/sys.old/kern/kern_fork.c /usr/src/sys/kern/kern_fork.c --- /usr/src/sys.old/kern/kern_fork.c Tue Feb 12 00:24:13 2002 +++ /usr/src/sys/kern/kern_fork.c Tue Feb 12 00:26:02 2002 @@ -93,6 +93,8 @@ }; #endif +int forksleep; /* Place for fork1() to sleep on. */ + static void init_fork_list(void *data __unused) { @@ -297,8 +299,8 @@ * processes, maxproc is the limit. */ uid = p1->p_ucred->cr_ruid; - if ((nprocs >= maxproc - 1 && uid != 0) || nprocs >= maxproc) { - tablefull("proc"); + if ((nprocs >= maxproc - 10 && uid != 0) || nprocs >= maxproc) { + tsleep(&forksleep, td->td_kse->ke_priority, "fork", hz / 2); return (EAGAIN); } /* @@ -318,6 +320,7 @@ * Back out the process count */ nprocs--; + tsleep(&forksleep, td->td_kse->ke_priority, "fork", hz / 2); return (EAGAIN); } Only in /usr/src/sys/kern/: kern_fork.c.orig diff -u -r /usr/src/sys.old/kern/subr_param.c /usr/src/sys/kern/subr_param.c --- /usr/src/sys.old/kern/subr_param.c Tue Feb 12 00:24:16 2002 +++ /usr/src/sys/kern/subr_param.c Tue Feb 12 00:35:40 2002 @@ -131,6 +131,7 @@ void init_param2(int physpages) { + int automaxproc; /* Base parameters */ maxusers = MAXUSERS; @@ -144,11 +145,25 @@ } /* + * In order to make sure that the system cannot be taken + * down by too many processes, we limit the number of + * processes so that process-related structures can + * only occupy at most 50% of system memory. + * + * XXX - The 32K value was arrived at experimentally, + * and may require changing if pre-proc memory usage + * changes substantially. + */ + automaxproc = (physpages / 2) / (32768 / PAGE_SIZE); + + /* * The following can be overridden after boot via sysctl. Note: * unless overriden, these macros are ultimately based on maxusers. */ maxproc = NPROC; TUNABLE_INT_FETCH("kern.maxproc", &maxproc); + if (maxproc > automaxproc) + maxproc = automaxproc; maxfiles = MAXFILES; TUNABLE_INT_FETCH("kern.maxfiles", &maxfiles); maxprocperuid = (maxproc * 9) / 10; Only in /usr/src/sys/kern/: subr_param.c.orig