I get how a normal fork bomb works, but I don't really understand why the & at the end of the common bash fork bomb is required and why these scripts behave differently:
:(){ (:) | (:) }; :
and
:(){ : | :& }; :
The former causes a cpu usage spike before throwing me back to the login screen. The latter instead just causes my system to freeze up, forcing me to hard reboot. Why is that? Both continually create new processes, so why does the system behave differently?
Both of the scripts also behave differently from
:(){ : | : }; :
which doesn't cause any problems at all, even though I would have expected them to be alike. The bash manual page states that the commands in a pipeline are already executed in a subshell, so I'm led to believe that : | : should already suffice. I belive & should just run the pipeline in a new subshell, but why does that change so much?
Edit:
Using htop and limiting the amount of processes, I was able to see that the first variant creates an actual tree of processes, the second variant creates all the processes on the same level and the last variant doesn't seem to create any processes at all. This confuses me even more, but maybe it helps somehow?
Best Answer
WARNING DO NOT ATTEMPT TO RUN THIS ON A PRODUCTION MACHINE. JUST DON'T. Warning: To try any "bombs" make sure
ulimit -u
is in use. Read below[a].Let's define a function to get the PID and date (time):
A simple, non-issue
bomb
function for the new user (protect yourself: read [a]):When that function is called to be executed works as this:
The command
date
is executed, then a "yes" is printed, an sleep for 1 second, then the closing commanddate
, and, finally, the function exits printing a new command prompt. Nothing fancy.| pipe
When we call the function like this:
Two commands get started at some time, the two end 1 second later and then the prompt returns.
That's the reason for the pipe
|
, to start two processes in parallel.& background
If we change the call adding an ending
&
:The prompt returns immediately (all the action is sent to the background) and the two commands get executed as before. Please note the value of "job number"
[1]
printed before the PID of the process3380
. Later, the same number will be printed to indicate that the pipe has ended:That is the effect of
&
.That is the reason of the
&
: to get processes started faster.Simpler name
We can create a function called simply
b
to execute the two commands. Typed in three lines:And executed as:
Note that we used no
;
in the definition ofb
(the newlines were used to separate elements). However, for a definition on one line, it is usual to use;
, like this:Most of the spaces are also not mandatory, we can write the equivalent (but less clear):
We can also use a
&
to separate the}
(and send the two processes to the background).The bomb.
If we make the function bite its tail (by calling itself), we get the "fork bomb":
And to make it call more functions faster, send the pipe to the background.
If we append the first call to the function after a required
;
and change the name to:
we get:Usually written as
:(){ :|:& }; :
Or, written in a fun way, with some other name (a snow-man):
The ulimit (which you should have set before running this) will make the prompt return quite quickly after a lot of errors (press enter when the error list stops to get the prompt).
The reason of this being called a "fork bomb" is that the way in which the shell starts a sub-shell is by forking the running shell and then calling exec() to the forked process with the command to run.
A pipe will "fork" two new processes. Doing it to infinity causes a bomb.
Or a rabbit as was originally called because it reproduces so quickly.
Timing:
:(){ (:) | (:) }; time :
Terminated
real 0m45.627s
:(){ : | :; }; time :
Terminated
real 0m15.283s
:(){ : | :& }; time :
real 0m00.002 s
Still Running
Your examples:
:(){ (:) | (:) }; :
Where the second closing
)
separates the}
is a more complex version of:(){ :|:;};:
. Each command in a pipe is called inside a sub-shell anyway. Which is the effect of the()
.:(){ : | :& }; :
Is the faster version, written to have no spaces:
:(){(:)|:&};:
(13 characters).:(){ : | : }; :
### works in zsh but not in bash.Has a syntax error (in bash), a metacharacter is needed before the closing
}
,as this:
[a] Create a new clean user (I'll call mine
bize
). Login to this new user in a console eithersudo -i -u bize
, or:Check and then change the
max user processes
limit:Using only 10 works as is only one solitary new user:
bize
. It makes easier to callkillall -u bize
and get the system rid of most (not all) bombs. Please do not ask which ones still work, I will not tell. But still: Is quite low but on the safe side, adapt to your system.This will ensure that a "fork bomb" will not collapse your system.
Further reading: