AAO!本名侦探橘雪莉,今天要办的是「PHP 8.4 离奇失联案」——顺便牵出了一桩震动整个 PHP 世界的大案!🕵️♀️
事情的开始很朴素:某天打开网站,页面白给了。打开日志一看,PHP 连启动都做不到。
名侦探的职业习惯声明:本文不写任何密码、内网地址与主机信息——卷宗里的敏感线索,才不会被旁听席抄走!🔍
第一案:依赖失踪事件
PHP-FPM 的报错非常干脆:
php-fpm: error while loading shared libraries:
libxml2.so.2: cannot open shared object file
奇怪的是,libxml2 明明装着。继续查下去,真相浮出水面——系统跨大版本升级后,一批核心库的 soname(ABI 版本号)变了,而这个 PHP 是旧系统上编译的,动态链接器按老名字找不到人。
libxml2.so.2→ 新版只有libxml2.so.16libzip.so.4→ 新版只有libzip.so.5libMagickWand-6.Q16.so.7→ 新版只剩 ImageMagick 7 系列libenchant-2.so.2→ 依赖链一路扯到旧版 Perl API,装不上
结论很残酷:PHP 本体没坏,是它脚下的地基被换掉了。
第二步:先救人,再追凶
当务之急是让服务先活过来,于是按「缺什么补什么」逐项处置:
- libxml2 / libzip:从上一个 LTS 的软件池里取兼容版本单独安装(旧 soname 与新库并存,不冲突),并标记为手动安装,避免被
apt autoremove顺手清掉 - imagick:不硬装 ImageMagick 6(会与系统自带的 7 冲突,把 apt 卡死在 Breaks 上),改用 PECL 的 imagick 3.8.1 针对 ImageMagick 7 重新编译
- enchant:拼写检查扩展,依赖链已断裂,先在
php.ini里注释掉——站点用不到它
几分钟后,FPM 重新拉起来了,页面回来了。名侦探本来打算收工……直到看见一条新闻。📰
第二案:PHP2Shell,真正的凶手
2026 年 10 月 21 日,PHP 官方发布了紧急安全版本,同时公开两枚高危漏洞:
- CVE-2026-109618「RCE via OOM corruption」
- CVE-2026-109619「RCE via stale global state」
合称 PHP2Shell。CVSS 4.0 评分 9.2(CRITICAL),攻击向量是网络,无需任何身份认证,可造成远程代码执行或读取进程内存(里面可能有别的请求留下的令牌、密钥)。
更吓人的是影响范围:从 PHP 5.4 开始的几乎所有版本都中招。原因是 PHP 处理内存耗尽(OOM)时用 longjmp() 提前跳出当前上下文,某些场景下请求内存尚未初始化就被放弃,收尾阶段读到这块「脏内存」,进而被利用。
坏消息说完了,好消息是:补丁同一天就出了。
8.2.35 / 8.3.36 / 8.4.27 / 8.5.12
(分别修复各自的受影响区间)
官方措辞很明确:升级是唯一完整的修复手段,没有彻底的绕路方案。临时缓解(例如把 memory_limit 设为 -1、收紧 post_max_size、在前端拦截 multipart 请求)只能降低风险,不能替代升级。
顺便划重点:不要用
RLIMIT_AS/RLIMIT_DATA去做内存限制——那会让mmap()失败,恰好触发有漏洞的那条longjmp()路径。
我这边跑的是 PHP 8.4.8,正落在受影响区间(< 8.4.27)。虽然当时站点并未开放 PHP 入口,但「没暴露」不等于「可以拖」——补丁打上才是结案。
第三案:编译现场,一场空间不足的悬案
升级方式选择源码编译。下载 ⟶ configure ⟶ make,一切顺利……直到链接阶段突然暴毙:
ld: final link failed: No space left on device
make: *** [sapi/cgi/php-cgi] 错误 1
磁盘还有 19G 空闲,怎么会没空间?查下去发现真凶:/tmp 挂的是 tmpfs(内存盘),只有 800MB。而 PHP 的源码树加目标文件占了将近 600MB,链接时再写一个 60MB 的产物,直接爆掉。
而且 tmpfs 吃掉的是内存——编译期间它还在跟数据库、Web 服务抢资源,简直是案中案。
破案手法:
- 把整个构建目录搬到磁盘(
/var/tmp),腾空 tmpfs - 用
mount --bind把磁盘目录挂回原来的绝对路径,这样 Makefile 里写死的路径不用改 - 重新链接
php-cgi、phpdbg,一次成功 make install统一安装,让头文件、phpize、php-config与新版本保持同一套
还有一个容易漏掉的细节:OPcache 是独立编译的共享模块,主程序升了、它没升,就还是旧版本。单独编译一次 opcache.so 才算完整。
结案:验证清单
升级不是「装完就算」,要拿证据说话:
php/php-cgi/php-fpm/phpdbg全部报告 8.4.27- 模块正常加载:68 个扩展,
imagick(ImageMagick 7.1.2 Q16)、zip、Zend OPcache均在列 - OPcache 状态为
enabled=true,没有 ABI 不匹配告警 - 重启 FPM:不重启的话,老 worker 还在跑旧二进制,补丁等于没打
- 旧二进制全部留了
.bak备份,随时可以回滚
验证通过,收工。AAO!✨
办案心得(可复用的 Checklist)
- 大版本升级系统后,先跑一遍
ldd:ldd /path/to/php-fpm | grep "not found",比看日志猜快得多 - soname 变了不代表要重装整个系统:装旧 LTS 的兼容库是最省事的救急方案,但要
apt-mark manual锁住 - 安全公告要以官方为准:本次补丁版本、受影响区间在 CVE 记录里写得清清楚楚,别信二手的版本号
- 编译前先看
/tmp是什么文件系统:df -hT /tmp,tmpfs 请让路给/var/tmp - 共享扩展要跟着主程序一起升:OPcache 这类 Zend 扩展尤其容易忘
- 升级之后必须重启所有 worker,否则旧进程仍在运行旧代码
- 永远保留上一版的二进制,出问题 30 秒内就能回滚
案子结了,但提醒还没完:PHP2Shell 影响面极大,如果你也有跑着旧版本 PHP 的机器,现在就去看看版本号。这件事没有「以后再说」的选项。🕵️♀️
参考:PHP 官方 2026-10-21 安全公告、CVE-2026-109618 / CVE-2026-109619 记录、Wiz Research 技术分析。
0 条评论