综合

python信息炸弹

时间:2010-12-5 17:23:32  作者:知识   来源:百科  查看:  评论:0
内容摘要:你写过那种一运行就让同事电脑风扇狂转的脚本吗?我写过。三行代码,内存占用直接飙到8个G,任务管理器里的曲线像打了肾上腺素。那台i5的笔记本当场卡死,鼠标指针变成一块石头。后来我管这类东西叫“信息炸弹”

你写过那种一运行就让同事电脑风扇狂转的信息脚本吗?我写过。三行代码,炸弹内存占用直接飙到8个G,信息任务管理器里的炸弹曲线像打了肾上腺素。那台i5的信息笔记本当场卡死,鼠标指针变成一块石头。炸弹后来我管这类东西叫“信息炸弹”——不是信息病毒,不破坏文件,炸弹纯粹靠合法的信息Python语法把系统资源吃干抹净。

原理简单得可笑。炸弹a = [0] * 10**8,信息一个列表,炸弹八亿个零。信息Python的炸弹整数对象在64位系统上占28字节,光这一行就吃掉2.2G内存。信息再来一行b = a[:],复制一份,4.4G。循环十次?机器直接跪。更阴的招是用字符串拼接:s = ""; for i in range(10**7): s += "x"。每次拼接都创建新对象,旧对象等垃圾回收,内存碎片像打碎的玻璃渣。跑上三十秒,交换分区开始疯狂读写,硬盘灯长亮不灭。

我见过最损的一个例子是递归。有人写了个计算斐波那契数列的函数,没加记忆化,参数传个40。调用栈瞬间膨胀到几千层,Python解释器直接抛RecursionError。但要是把递归深度限制调高呢?sys.setrecursionlimit(10**6),然后传50。栈帧把内存撑爆,进程被OOM killer干掉。Linux内核的日志里留下一行冷冰冰的“Out of memory: Killed process 12345 (python3)”。

这些代码本身没有恶意。它们只是逻辑上正确、但资源消耗指数级膨胀的普通程序。问题出在Python的抽象层太厚。你写list(range(10**9)),以为只是生成一串数字,实际上解释器要分配十亿个PyObject结构体,每个都带引用计数和类型指针。C语言里一个int就4字节,Python里同样的数字要28字节。七倍的差距,乘以十亿就是灾难。

有人用这个整蛊。把while True: [0]*10**6塞进同事的启动脚本,开机三分钟死机。或者更隐蔽的,写个Flask接口,收到特定请求就执行内存炸弹。服务器上的Docker容器内存限制设了2G,一个请求过去,容器直接重启。日志里只有“Killed”三个字母。排查半天,最后发现是某行json.loads解析了一个深度嵌套的恶意JSON,递归解析把栈撑爆了。

防御手段倒是有。资源限制模块resource能设内存上限,resource.setrlimit(resource.RLIMIT_AS, (2**31, 2**31)),超过2G直接报MemoryError。或者用tracemalloc监控内存分配,找出哪行代码在疯狂申请。但大多数人写脚本时根本想不到这些。他们觉得Python慢是慢,但不会把机器搞崩。直到某天跑了个pd.concat合并两百个CSV文件,每个文件一百万行,pandas在内存里建了个中间表,16G内存条瞬间见底。

我自己的教训来自一个正则表达式。写了个re.match(r'(a+)+b', 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'),经典的灾难性回溯。输入三十个a,匹配器尝试了2^30种组合,CPU单核跑满,风扇狂转,进程卡死。等了十分钟没出结果,只能kill -9。后来查资料才知道,这种正则的复杂度是指数级的。Python的re模块没有超时机制,只能靠信号或者子进程隔离。

信息炸弹的可怕之处在于它穿着合法代码的外衣。没有eval,没有exec,没有os.system。静态扫描工具扫不出问题,因为语法完全正确。它只是利用了Python对象模型的重量级特性和解释器的资源管理漏洞。一个刚学Python的人可能无意中写出这种代码,比如用列表推导式生成超大矩阵,或者用str * 10**9构造字符串。然后程序崩溃,他以为是电脑坏了。

下次你写循环的时候,看一眼里面的对象大小。乘以迭代次数。超过内存条容量的话,要么改算法,要么准备好重启机器。

最近更新
热门排行
copyright © 2026 powered by 烟消火灭网   sitemap