17岁里面的四哥是谁:bash版本的fork bomb分析

来源:百度文库 编辑:偶看新闻 时间:2024/04/19 11:49:39

这个fork bomb提取自此处:

:(){ :|:& };:

SA经常用这个脚本来测试用户进程限额设置是否正确(通过配置/etc/security/limits.confPAM)。

今天试了一把,真把系统搞挂了,最后不得不由SA重启机器-_-b

下面解析下这13char:

:()

定义一个函数,名叫:,无参。

{ :|:& };

:的函数体。

:

调用该函数。

再看函数体部分,如果只是:,那只是个简单的尾递归程序,如果bash优化得当不会有任何问题。

再变复杂一些:

: => :|:

这样每递归一次,都会额外再创建2个进程(到这里其实就已经会不断fork,算是半个fork bomb),AB,且ASTDOUTBSTDIN,且caller会执行waitpid等待这两个进程挂掉,且caller挂掉会带走它们俩。

考虑第一次递归的情况,加上本身1个,总共会有3个进程,证明一下:

vi test:

#!/bin/bash

a(){
    a
};

b(){
    b
};


a|b

./test执行后,在另一个窗口ps -ef|grep test,可以看到确实如此:

root     17001 16625  0 20:53 pts/0    00:00:00 /bin/bash ./test
root     17002 17001 99 20:53 pts/0    00:00:01 /bin/bash ./test
root     17003 17001 99 20:53 pts/0    00:00:01 /bin/bash ./test

还可以看到函数体中管道两边创建的进程的爹都是caller: 17001

最后:

:|: => :|:&

加上&是为了让爹死了之后自己还能活。

shell命令如果没有&,执行过程是:

  1. shellfork一个进程A,这个进程收到SIGHUP信号时会退出。
  2. A中调用execve执行任务
  3. shell执行waitpid等待A挂掉。

然后是带&的情况:

  1. shellfork一个进程B,并执行execve开始干活;该进程收到SIGHUP时不会退出,只是改认init(PID=1)为爹。
  2. shell不等待B挂掉继续干自己的活。

所以:|:&的净效果是,再fork一个进程B,工作内容是:|:,这时候caller没活干了也不用等B挂,所以直接退出。B在做:|:时又要再创建2个进程干同样的活,且要等它们挂。

这样1轮递归的效果是1个进程变成3个;到第2轮时B创建的2个进程会导致另外6个进程被创建,然后第一轮的3个都退出,还剩6个;到第3轮时,上一轮6个进程中的4个会导致另外12个进程被创建,然后这6个都退出,剩12个;之后依次类推。

容易发现规律是1->3->6->12->24,之后都是倍增,几何级数。且kill一个爹最多带走2个子,APM再高也来不及只好重启。