PHP2Shell漏洞中longjmp异常退出本质是OOM时绕过清理强行跳转,导致半初始化内存被关闭流程误用,引发UAF等内存破坏;其高危性在mod_php和PHP-FPM等复用进程场景尤为突出。

PHP2Shell漏洞中的longjmp异常退出机制,本质是PHP在内存耗尽(OOM)时绕过正常执行流程、强行跳转导致状态不一致的底层行为。它不是设计用来做错误处理的“安全出口”,而是暴露了内存生命周期管理的关键缺陷。
longjmp在PHP中的真实作用场景
PHP内核用setjmp/longjmp实现一种轻量级的非局部跳转,类似C语言里的“紧急逃生通道”。当检测到内存不足(超出memory_limit),PHP不走常规的函数逐层返回路径,而是直接调用longjmp回跳到之前用setjmp保存的上下文点——通常是请求初始化刚起步的位置。
- 这个跳转不会清理已分配但未完成初始化的请求内存
- 也不会重置正在使用的全局结构体指针(如
EG(error)、PG(http_globals)等) - 后续的请求关闭流程(shutdown phase)仍会照常运行,并尝试访问这些残留的、半初始化或已释放的数据
为什么这会导致远程代码执行
问题不在于longjmp本身,而在于它被用在了一个不该“跳过清理”的关键环节:
- 攻击者发送一个特制的
multipart/form-data请求,比如含大量嵌套字段名、空文件控件或超长header,让PHP在解析过程中快速耗尽内存 - OOM触发
longjmp,请求中途“断电”,但部分结构体字段已写入、部分尚未写入,形成脏状态 - 关闭阶段遍历这些结构体时,读取到无效指针或越界偏移,造成内存破坏(如UAF、堆喷射)
- 攻击者通过精心构造的请求序列,把这种破坏转化为可控的代码执行
和普通C程序异常处理的区别
一般C项目中,longjmp只用于极少数明确受控的错误恢复逻辑,且开发者会主动确保跳转前后资源一致性。而PHP把longjmp嵌入到了核心执行引擎的OOM路径中,且未对所有可能被跳过的清理步骤做防御性加固——特别是没考虑多请求复用进程(如mod_php、PHP-FPM)下,残留状态会被下一个请求误读。
立即学习“PHP免费学习笔记(深入)”;
- 这不是“异常处理机制”,而是“异常中断后缺乏兜底”的表现
- 它绕过了C++风格的栈展开(stack unwinding),也没有RAII式的自动资源回收
- 修复方式不是禁用
longjmp,而是补全跳转前的状态冻结与跳转后的强制重置逻辑
实际影响最严重的运行环境
该机制的危害程度高度依赖PHP的部署模型:
- Apache mod_php:每个子进程长期服务多个请求,状态复用最频繁,风险最高
- PHP-FPM:worker进程同样复用,CVE-2026-109619已在该环境下验证成功
- CGI模式:每次请求新建进程,无状态复用,基本不受影响
所以补丁不只是修一个函数,而是重构了整个请求内存生命周期中OOM路径的清理契约。



















