Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How does this other version of the bash fork bomb work?

I get the general idea of how this common version

:(){ :|:& };:

of the bash fork bomb works.

However, I've seen another version (for bash in particular)

#!/bin/bash
$0 &
$0 &

on the Wikipedia fork bomb article and in an SO answer to a closed duplicate of the original fork bomb question I mentioned above.

I'm looking for an explanation of how the second (perhaps less common) version of the fork bomb works. I've commented the code below with my current understanding of what it does, but I don't really get how it achieves infinite recursion in the way that the other version of the bash fork bomb does (probably due to my poor understanding of bash processes and backgrounding).

#!/bin/bash    # Specifies the location of the executable
               # to use in executing this script.
$0 &           # Duplicates (executes a new instance
               # of) the current running process ('$0')
               # (which would be the invocation of bash used
               # to start running the script, right?)
               # and background it ('&').
$0 &           # Do the same as above, where $0 would be
               # equivalent to the initial call to bash
               # used to start the script, right? Or, would
               # it be the backgrounded call to bash from the
               # second line? I'm leaning towards the former.

EDIT: My revised understanding of the script (at least at the level of abstraction I'm worried about at the moment) is below in the form of commented code. I've left my original commented code up for future viewers who might have the same initial misunderstandings I had. Assume the script lives in bomb.sh.

#!/bin/bash    # Script will execute using /bin/bash executable.
$0 &           # The current process (P) will spawn a child process (C1)
               # by invoking the command that spawned P
               # (/bin/bash ./bomb.sh). This makes the script recursive.
               # & allows processes to run in the background
               # (allowing process death and avoiding a potential
               # process limit).
$0 &           # Process P spawns a second child process (C2), also 
               # in the background, which gives us the exponential growth
               # (in base 2) that we want per level of recursion.
like image 443
stefanbehr Avatar asked Jan 19 '23 03:01

stefanbehr


1 Answers

If you break this bomb down a little it might make more sense. Change this to:

#!/bin/bash
$0

This bomb will spawn a new copy of the shell script over and over again:

$ ps auxw | grep pts/2
sarnold   2410  0.0  0.1  24840  6340 pts/2    Ss   Nov17   0:01 bash
sarnold  17280  0.0  0.0  12296  1600 pts/2    S+   18:01   0:00 /bin/bash ./bomb.sh
sarnold  17281  0.0  0.0  12296  1600 pts/2    S+   18:01   0:00 /bin/bash ./bomb.sh
sarnold  17282  0.0  0.0  12296  1600 pts/2    S+   18:01   0:00 /bin/bash ./bomb.sh
sarnold  17283  0.0  0.0  12296  1596 pts/2    S+   18:01   0:00 /bin/bash ./bomb.sh
sarnold  17284  0.0  0.0  12296  1600 pts/2    S+   18:01   0:00 /bin/bash ./bomb.sh
...
$ ps auxw | grep pts/2 | wc -l
2077

Each old one is essentially "dead" -- waiting to reap the exit status from the executed child. Of course, that won't happen until one of the executed children finally cannot execute due to process limits. (Which incidentally reminds me that perhaps setting an nproc rlimit would be a good idea before playing much further.)

So this process tree looks like this:

1
 2
  3
   4
    5
     6

If you add the & to the end of the command, you'll see that older ones can eventually get scheduled and die. New ones form just as rapidly though, so this causes pretty significant churn and helps reduce the chance that it'll just end when the maximum number of processes have been spawned. The tree will look more like this:

1
 2
  3

    5
     6

       8

Adding the second $0 & line will cause the tree to look a little different:

           1
        2     3
      4   5 6   7

Except it probably won't be this nice -- the second process might start the third, rather than the first one starting the third. It'll probably look mighty chaotic.

Each "layer" will double the size of the previous layer, on the whole, but execve(2) can only be called so often -- the trick is that this bomb will probably force far more process context switches than the simpler bombs, and all those TLB flushes are going to significantly impact the performance of the system. Because parents will be essentially randomly dying off after executing both children, init(8) will have far more processes re-parented. All this will increase the number of signals sent to init(8) to clean up, which would make it harder for an admin to log in and fix the problem.

like image 194
sarnold Avatar answered Jan 30 '23 01:01

sarnold