http://www.perlmonks.org?node_id=254078

as i just fork-bombed my router with a botched attempt to speed up a quick hack, i thought i'd take this opportunity to offer some wisdom on safe handling of fork() for those without the scars of personal experience. *grin*

if you aren't familiar with the concept, in a nutshell, fork-bombs are class of 'rabbit' processes (they suck up pools of pids, not just one) that have no cap on the number of of pids they syphon, or commonly have a cap far in excess of the size of the number of pids existing/possible in the system -- the modern cancer approach, grow as much and as fast as possible, and do it in a multi-threaded way.

if you have made acquaintence with fork-bombs in the past, you can ingore this, and stick to your line: "been there, done that, bought the t-shirt".


The Problem:

> is there anything that I can do to fix it?

pull the plug. the pid-space is full, nothing on the system can spawn new processes for _anything_ owning to system being all forked up. (sorry) that includes spawning processes for new connections, spawing new shells, su, shutdown, halt, etc.

there is a kernel module you can install to prevent fork-bombs, if it isn't already too late to do that. if you have privs to install kernel modules that is...


The Cause

while( ){ fork(); }

this and all isomorphisms, including loops with conditions, and loops with fork() in a conditional block, (if the condition in question is incorrect in a manner that always evaluates as true) are fork-bombs if the loop runs more than about a dozen times.


Technical Explanation

one codes fork() as:

if( fork() ){ # code } else { # code }

and this evaluates as both true AND false (one in the old process, one in the new, respectively). the kicker is, both processes continue normally after the end of the if() block, (with whatever differences they acquired in the course of the block).

that means a loop written:

while( ){ if( fork() ){ # code else{ # code } }

will run twice on the first pass, four times on the second, eight on the third, etc. producing a cheap way to test binary permutations, and a very easy way to use 1048576 threads in just 20 iterations.

this, it should be noted, is not a valid approach to programming anything short of a quantum computer. (for which, i understand, it is the only method. -- go fig.)


The Solution

fork() should never be used in a loop, except as follows:

while( ){ if( fork() ){ # code } else { # code exit; # <--- !!!! } }

which will produce exaclty one thread running the loop, and one additional thread per iteration, thus totalling only 21 threads for 20 iterations in contrast with the above.

for what it's worth, this is easiest for me to remember simplified as

"if fork, else exit"

of course, isomorphisms of the above will work, so this will also fine:

while( ){ unless( fork() ){ #code exit; } }

you may notice i only ever put the exit; in the false portion of the conditional, this may not matter to everyone, but technically fork() returns true for the parent and false for the child, and i'm in favor of orderly process management. *grin*

-- Xanatax.


"i'd like to see a positive LSD story, would that be newsworthy? just once? hear what it's all about? 'Today a young man on acid realized that all matter is merely energy condensed to a slow vibration, that we are all one consciousness experiencing itself subjectively, there no such things as death, life is only a dream and we are the imagination of ourselves. Here's Tom with the weather...'"
  --Bill Hicks

Replies are listed 'Best First'.
Re: fork-bomb!
by Juerd (Abbot) on Apr 29, 2003 at 22:10 UTC

    If fork returns false, don't just assume you're the child. The returned value can be undef, which means fork failed. If you fail to check for undef, your code has very strange bugs when forking fails (which happens under heavy load, when you exceed resources, etc).

    if (my $pid = fork) { # parent } elsif (defined $pid) { # child exit; } else { die $!; }
    or one of the 1e6 other ways to write that.

    Juerd # { site => 'juerd.nl', plp_site => 'plp.juerd.nl', do_not_use => 'spamtrap' }

Re: fork-bomb!
by Eimi Metamorphoumai (Deacon) on May 01, 2003 at 17:37 UTC
    I'm an admin on a few systems that get fork-bombed accidentally a few times a semester (big computers used by students in begining OS courses). There is something you can do if you catch it while it's going on, though, as long as you do a little planning ahead. On those machines I keep about four shells nested.
    exec tcsh set prompt="THIS IS YOUR LAST SHELL# " tcsh tcsh tcsh tcsh
    (cut and paste into the terminal). Then if I notice the machine is going to Hell, I can "exec top" and "exec pkill -u person" and things like that. Although you can no longer get any new pids, you're quite free to sacrifice shells for that one last desperation command. Just a little tip, in case anyone is interested.

      ok, _that_ was what is was trying to find info on! couldn't figure out how to burn the pids i still had in shells on things like kill or shutdown. thanks!

      the nested shells is a neat idea also... actually, that's really easy and convenient to setup with 'screen' also. i generally end up with at least eight shells running because of wonton use of windows in screen, so having spare pids is more likely than not.

      i just added an init line to the system to make screen start a half-dozen shells for the root account, so as long as i have a connection to the machine i will have those available for emergencies.

      -- Xanatax.

      One could also use psh and exploit the feature of being able to execute Perl code without forking.

      --
      David Serrano

Re: fork-bomb!
by TVSET (Chaplain) on Apr 29, 2003 at 23:36 UTC
    Nice node. Maybe you could have mentioned "ulimit -u" somewhere, since it's related, and it's one way of limiting the number of processes, which is usually installed on both production and development servers.

    Leonid Mamtchenkov

Re: fork-bomb!
by cgishack (Initiate) on Dec 22, 2004 at 08:18 UTC
    So ya.. my first experience with fork() was not good. I emailed myself 40,000 times.. Try waking up to that in the morning,, and also explain it to the sites host.. YIKES. Since then fork() has been great. Just wish i knew how to kill a process via browser if there is such a way.. is there ?
Re: fork-bomb!
by Abigail-II (Bishop) on Apr 30, 2003 at 22:33 UTC
    there is a kernel module you can install to prevent fork-bombs, if it isn't already too late to do that. if you have privs to install kernel modules that is...

    Yeah, the whole world is a VAX....

    Abigail