image.png

PHP 的内存管理、异常退出机制与 Session 上传进度功能之间,可能存在比单个内存错误更复杂的安全问题。本文围绕 CVE-2026-109618 和 CVE-2026-109619,梳理相关漏洞的成因、攻击链、修复思路与防护措施。

安全提示: 本文用于漏洞原理分析、版本核查和防御加固。具体漏洞是否可利用,取决于 PHP 版本、构建方式、运行环境及应用配置。请勿在未经授权的系统上测试漏洞。

一、漏洞概述

PHP 的内存分配器负责管理运行时分配的内存。当进程遇到严重内存错误时,运行时需要终止当前执行流程,避免程序继续使用已经损坏的内部状态。

问题在于,PHP 并非所有错误都会通过普通的函数返回值传递。一些致命错误会进入 bailout 机制,通过非局部跳转退出当前执行路径。如果内存分配器、Zend 引擎与扩展模块之间的状态没有正确恢复,异常退出就可能影响后续代码执行。

本文关注的两个漏洞编号为:

  • CVE-2026-109618:与 PHP 在特定异常退出路径中的内部状态损坏有关。
  • CVE-2026-109619:与前述异常状态在后续请求中的影响及潜在内存安全问题有关。

两者需要结合分析:第一个问题关注异常发生时的状态一致性,第二个问题关注异常状态如何进一步影响后续执行。

需要强调的是,漏洞编号、受影响版本、CVSS 评分和修复版本应以 PHP 官方安全公告及对应发行版公告为准。不要仅凭第三方文章中的版本号或评分判断系统是否安全。

二、理解 PHP 的 bailout 机制

2.1 普通错误与 bailout 的区别

普通错误通常可以通过返回值、异常或错误处理机制进行处理。例如:

1
2
3
4
5
$result = some_operation();

if ($result === false) {
// 处理失败
}

但 Zend 引擎中的部分致命错误无法按照普通业务逻辑继续执行。在某些路径上,PHP 会通过 bailout 机制退出当前执行流程。

从概念上看,相关调用链可能涉及以下组件:

1
2
3
4
5
6
7
8
9
10
11
12
13
flowchart TD
A["发生内存分配失败或其他致命错误"]
B["Zend 内存管理器"]
C["进入错误处理路径"]
D["触发 bailout 机制"]
E["非局部跳转,退出当前执行路径"]
F{"内部状态是否正确恢复?"}
G["正常清理并结束请求"]
H["可能残留不一致状态"]

A --> B --> C --> D --> E --> F
F -->|是| G
F -->|否| H

在源码分析中,可以重点关注以下符号:

  • zend_mm_safe_error()
  • zend_error_noreturn()
  • zend_bailout()

这些函数分别涉及内存管理错误、不可返回的错误处理路径以及 bailout 退出机制。具体调用关系会随 PHP 版本和代码路径而变化,因此不能将上述示意图理解为所有内存错误都会经过的固定调用链。

2.2 为什么非局部跳转需要特别谨慎

longjmp 一类的非局部跳转可以直接离开当前调用栈中的部分函数,而不必像正常返回那样逐层执行后续代码。

这意味着,如果某个函数在执行过程中修改了全局状态、内存分配器状态或请求上下文,那么异常退出时就必须确保这些状态仍然一致。

例如,假设一个组件正在执行以下操作:

  1. 修改内部状态;
  2. 分配或释放内存;
  3. 调用可能触发致命错误的操作;
  4. 依赖正常返回路径恢复状态。

如果第三步触发了 bailout,第四步就可能被跳过。如果必要的恢复逻辑没有安排在可靠的清理路径中,内部状态就可能残留在中间状态。

这类问题通常被称为异常安全性问题或状态一致性问题。当残留状态进一步影响内存生命周期时,就可能演变为更严重的内存安全漏洞。

三、CVE-2026-109618:异常退出导致的状态损坏

3.1 核心问题

CVE-2026-109618 所涉及的核心问题,是特定异常退出路径中 PHP 内部状态的一致性。

分析此类漏洞时,需要区分两个层面:

  • 错误本身: 内存分配失败或其他致命条件触发异常退出。
  • 安全影响: 异常退出后,内部状态是否仍然有效,后续执行是否会访问已经失效或不符合预期的对象。

内存不足本身并不必然意味着远程代码执行。真正需要关注的是,程序是否在异常退出后继续使用了被破坏的状态,以及攻击者能否通过输入控制错误发生的位置和后续执行过程。

3.2 OOM 与内存管理器

OOM 是 Out of Memory 的缩写,即内存耗尽。它可能由进程内存限制、系统可用内存不足、异常大的请求或内存使用失控等原因触发。

在 PHP 中,内存分配失败可能进入 Zend 内存管理器的错误处理路径。此时,引擎需要在终止执行、清理资源和维持内部状态一致性之间作出正确处理。

如果错误处理流程本身依赖额外的内存分配,或者在异常退出前修改了关键状态,就可能形成复杂的失败路径。

从安全审计角度看,应重点检查:

  • 错误处理函数是否可能再次触发内存分配;
  • 异常退出是否绕过必要的状态恢复;
  • 分配器状态是否会在请求结束后继续存在;
  • 后续请求是否会接触此前请求留下的对象或指针;
  • 相关扩展是否假设某些清理函数一定会正常执行。

这些问题并非 PHP 独有。在 C、C++ 等需要显式管理资源生命周期的运行时中,异常安全与资源清理同样是重要的安全审计方向。

3.3 为什么不能把所有 OOM 都视为可利用漏洞

即使可以通过请求触发内存耗尽,也不代表攻击者能够获得代码执行能力。

实际影响取决于多个条件:

  1. 目标是否运行受影响的 PHP 版本;
  2. 请求是否能够进入相关错误路径;
  3. 异常退出是否导致可观察的状态损坏;
  4. 损坏状态是否会在后续执行中被使用;
  5. 攻击者能否控制后续执行所依赖的数据;
  6. 运行环境是否存在额外的可利用条件。

因此,内存耗尽、进程崩溃、拒绝服务、内存破坏和远程代码执行是不同的安全影响,不能直接画等号。

四、CVE-2026-109619:跨请求状态与内存生命周期

4.1 为什么 PHP-FPM 的请求生命周期很重要

PHP-FPM 通常由多个长期运行的工作进程处理 HTTP 请求。与每个请求都启动一个全新进程的模型不同,PHP-FPM 工作进程在处理完一个请求后,可能继续处理其他请求。

其基本流程如下:

1
2
3
4
5
6
7
8
9
10
flowchart TD
A["PHP-FPM Worker 启动"]
B["处理请求 A"]
C["请求 A 结束与清理"]
D["处理请求 B"]
E["请求 B 结束与清理"]
F["继续处理后续请求"]

A --> B --> C --> D --> E --> F
F --> B

这种设计能够降低进程启动开销,但也要求 PHP 在请求边界正确清理请求级状态。

如果某次请求在异常退出过程中未能完成预期的清理,且受影响的状态没有在下一次请求开始前被重置,那么后续请求就可能遇到不一致的运行环境。

这也是分析长期运行服务中的状态问题时,必须考虑请求复用机制的原因。

4.2 Use-after-free 的风险

Use-after-free(UAF,释放后使用)是指程序释放了一块内存后,仍然通过旧指针访问这块内存。

一个简化的示例:

1
2
3
4
5
6
7
8
9
10
char *ptr = malloc(64);

if (ptr == NULL) {
return;
}

free(ptr);

/* 错误:ptr 指向的内存已经释放 */
ptr[0] = 'A';

在真实漏洞中,情况往往更复杂:指针可能保存在对象字段、全局状态、扩展内部结构或其他间接引用中。

如果请求 A 在异常路径中留下失效引用,而请求 B 又在不合适的时机使用该引用,就可能产生跨请求的内存安全问题。

不过,需要注意:不能仅凭“跨请求状态残留”就断言一定存在 UAF,更不能直接推导出 RCE。 必须结合具体版本的源码、补丁差异和可重复的测试结果确认。

4.3 Session 上传进度功能的相关性

PHP 的 Session 上传进度功能用于在文件上传过程中记录进度信息。相关配置通常包括:

1
session.upload_progress.enabled = On

该功能可能在请求处理过程中参与 Session 状态更新,并与 multipart/form-data 请求及文件上传流程发生交互。

在分析涉及上传进度的漏洞时,需要检查:

  • 上传进度功能是否启用;
  • 请求是否包含符合条件的 multipart 数据;
  • Session 状态何时初始化和清理;
  • 内存分配失败是否可能打断正常处理流程;
  • bailout 之后是否仍有状态被后续请求访问。

这些因素可以帮助解释漏洞的触发条件,但不能据此认定所有启用上传进度功能的服务器都可被远程利用。

五、从状态损坏到远程代码执行:需要哪些条件

远程代码执行(RCE)意味着攻击者能够让目标系统执行其控制的代码。这通常需要一条完整的利用链,而不仅仅是触发一次错误。

概念上,潜在利用过程可能包含以下阶段:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
flowchart TD
A["攻击者可控的请求"]
B["满足漏洞触发条件"]
C["进入异常退出路径"]
D["可能出现状态未正确恢复"]
E["后续执行可能访问失效状态"]
F["可能形成内存安全问题"]
G{"是否满足进一步利用条件?"}
H["可能导致拒绝服务或进程崩溃"]
I["潜在远程代码执行"]

A --> B --> C --> D --> E --> F --> G
G -->|否| H
G -->|是| I

这只是用于理解漏洞风险的抽象模型,不代表每个受影响版本都能完成所有阶段。

实际利用难度还可能受到以下因素影响:

  • PHP 分支版本和具体补丁状态;
  • CPU 架构与编译器行为;
  • PHP 的编译选项;
  • PHP-FPM 的进程管理和请求分配方式;
  • 扩展模块及其内存分配行为;
  • 操作系统安全机制;
  • ASLR、NX 等缓解措施;
  • 请求是否能够稳定地进入预期执行路径。

因此,漏洞的严重程度与实际利用成功率是两个不同的问题。安全管理者应优先修复受影响版本,而不是以“暂时没有可用利用程序”为理由延迟更新。

六、官方补丁应重点检查什么

分析安全补丁时,不能只看提交标题或新增的几行代码。更可靠的方法是同时检查漏洞公告、代码差异、测试用例和发行说明。

可从以下几个方面理解修复思路。

6.1 保证异常退出路径的状态一致性

对于可能触发 bailout 的代码,需要检查在错误发生时哪些状态已经发生变化,以及哪些恢复步骤可能被跳过。

可靠的修复应避免程序在异常退出后保留无法安全使用的内部状态,并保证后续执行不会依赖已经失效的对象或数据。

6.2 将危险操作推迟到安全执行点

运行时中的某些操作不适合在任意执行位置完成。例如,错误处理过程中如果立即执行复杂的清理或再次分配内存,可能进一步增加状态管理难度。

因此,修复方案可能涉及延迟处理某些错误状态,并在引擎能够安全处理的执行点完成后续操作。

在阅读补丁时,应以具体版本的源代码为准,确认其实际使用的机制、状态变量和执行时机,而不要仅凭概括性描述推断内部实现。

6.3 增加回归测试

回归测试用于验证漏洞修复没有被后续修改破坏。

对于此类问题,测试至少应关注:

  • 异常退出后 PHP 是否能够正确结束当前请求;
  • 内存管理器状态是否仍然一致;
  • 后续请求是否可以正常处理;
  • Session 上传进度等相关功能是否仍能正常工作;
  • 修复是否引入新的崩溃、资源泄漏或行为回归。

应优先使用 PHP 官方仓库中的安全公告、补丁提交和测试用例作为依据。

七、如何确认服务器是否受影响

仅检查 PHP 主版本号通常不够。发行版可能会将安全补丁回移到较旧的软件包版本,因此还需要检查具体软件包及其安全更新记录。

7.1 查看 PHP 版本

在服务器上执行:

1
2
php -v
php --ini

前者查看命令行 PHP 版本,后者显示配置文件加载情况。

如果服务器使用 PHP-FPM,还应检查实际运行的 FPM 版本:

1
php-fpm -v

不同发行版的可执行文件名可能不同,例如可能使用带版本后缀的命令。也可以通过服务管理器查看实际运行的进程和服务。

注意:命令行 PHP 与 PHP-FPM 可能不是同一版本或使用不同配置。 对 Web 服务进行排查时,应以实际处理请求的 FPM 实例为准。

7.2 查看运行中的 PHP-FPM 服务

使用 systemd 的系统可以执行:

1
systemctl --type=service --all | grep -i php

确认服务名称后,再查看具体服务状态:

1
systemctl status php-fpm

如果服务名称不同,应替换为实际名称。

还可以检查正在运行的进程:

1
ps -ef | grep '[p]hp-fpm'

7.3 核查官方安全公告

建议依次确认:

  1. 官方公告列出的受影响版本;
  2. 官方公告列出的修复版本;
  3. 当前 PHP 分支是否仍处于安全支持周期;
  4. 操作系统发行版是否提供回移补丁;
  5. 当前安装的软件包是否已包含相关修复。

不要直接将上游版本号与发行版的软件包版本进行字符串比较。对于 Debian、Ubuntu、RHEL 等发行版,应结合各自的安全公告和软件包变更记录判断。

八、修复与临时缓解建议

8.1 首选方案:升级到包含修复的版本

优先使用 PHP 官方提供的已修复版本,或操作系统发行版提供的、明确包含相关安全修复的软件包。

升级前建议:

  1. 确认当前 PHP-FPM 与扩展模块的兼容性;
  2. 在测试环境验证主要业务;
  3. 备份配置文件;
  4. 通过软件包管理器完成升级;
  5. 重启或重新加载相关服务;
  6. 确认实际运行的 PHP-FPM 已使用更新后的二进制文件。

对于生产环境,不建议直接从未经验证的第三方来源下载 PHP 二进制文件覆盖现有安装。

8.2 临时关闭 Session 上传进度功能

如果官方公告或补丁分析确认该功能与目标环境的风险相关,并且业务不依赖它,可以评估关闭:

1
session.upload_progress.enabled = Off

修改前应确认 PHP-FPM 实际加载的配置文件,并在修改后检查配置是否生效。

该措施属于针对特定功能路径的风险降低措施,不能替代升级补丁,也不能保证消除所有相关漏洞路径。

8.3 限制上传请求

如果业务不需要大文件上传,可以考虑限制请求体大小和上传文件大小:

1
2
3
; php.ini 示例,数值应按业务需求调整
post_max_size = 16M
upload_max_filesize = 16M

同时,应在 Nginx 或其他前端 Web 服务器上配置请求体大小限制。例如:

1
client_max_body_size 16m;

这些限制可以降低异常大请求带来的资源消耗,但不能保证阻止所有能够触发内存错误的请求。

8.4 合理设置 PHP-FPM 工作进程回收策略

PHP-FPM 的 pm.max_requests 用于限制单个工作进程在被重新生成前处理的请求数量。

例如:

1
pm.max_requests = 500

实际值应结合应用的内存使用情况、请求量和性能需求调整。

较小的值可以缩短单个工作进程的存活时间,但会增加进程重建开销。它不能替代正确的请求清理逻辑,也不能保证在两个请求之间发生的状态问题不再出现。

不建议在缺少证据时一律设置为 1,因为这可能显著增加进程创建开销。

8.5 不要为了绕过 OOM 而盲目取消内存限制

PHP 的 memory_limit 用于限制脚本可使用的内存。针对 OOM 问题,盲目提高限制甚至设置为无限制,可能让异常请求消耗更多服务器资源。

例如,以下配置会取消 PHP 脚本的内存限制:

1
memory_limit = -1

除非有明确的业务需求和充分的资源隔离,否则不建议将其作为漏洞缓解措施。对于可能涉及异常内存状态的问题,应优先修复代码路径,而不是让进程能够消耗更多内存。

九、适用于生产环境的排查清单

可以按照以下顺序开展检查:

  • 确认实际处理 Web 请求的 PHP-FPM 版本。
  • 对照 PHP 官方公告与操作系统发行版安全公告。
  • 确认是否已安装包含修复的更新包。
  • 检查 session.upload_progress.enabled 的实际值。
  • 核查 Nginx、反向代理和应用层的请求大小限制。
  • 检查 PHP-FPM 工作进程的内存使用和异常退出日志。
  • 在测试环境验证升级后的上传、Session 和核心业务流程。
  • 升级后确认实际运行的服务已加载新版本。
  • 如果怀疑已经遭到利用,保留日志、进程信息和相关证据,并按事件响应流程开展调查。

不要仅凭日志中出现 Allowed memory size exhausted 就认定服务器遭到攻击。该信息也可能由正常业务中的内存使用超限导致,需要结合请求记录、异常频率、进程状态及其他证据综合判断。

十、参考资料

以下链接用于核查漏洞公告、源代码与修复提交。阅读时应优先确认页面中的发布日期、受影响版本和修复版本,并注意不同分支的回移补丁可能存在差异。

  1. PHP 官方安全公告:GHSA-w8hw-w9fx-j79w
  2. PHP 官方安全公告:GHSA-6wmf-4rv5-p3rp
  3. PHP 源码修复提交:506e87fa7dcd
  4. PHP 8.4 分支相关修复提交:31c0ea7d
  5. PHP 8.3 分支相关修复提交
  6. PHP 8.5.12:Zend 内存管理器源码
  7. PHP 8.5.12:PHP 主运行时源码
  8. PHP 8.5.12:Session 扩展源码
  9. PHP 8.5.12:Zend 测试目录
  10. Wiz:PHP2Shell 与 PHP RCE 相关分析
  11. CERT-EU 安全公告

总结

CVE-2026-109618 与 CVE-2026-109619 所涉及的关键问题,在于异常退出机制、内存管理状态和长期运行的 PHP-FPM 工作进程之间可能存在复杂的交互。

分析这类漏洞时,需要区分三个层次:

  1. 触发条件: 哪些输入和运行状态能够进入异常路径。
  2. 内存安全影响: 异常退出是否导致状态损坏、失效引用或其他内存错误。
  3. 实际可利用性: 攻击者是否能够在目标环境中进一步控制执行流程。

对服务器管理员而言,最可靠的应对措施仍然是核对官方安全公告、升级至包含修复的软件包,并在升级后验证实际运行的 PHP-FPM 实例。上传大小限制、关闭非必要功能和调整工作进程回收策略可以作为辅助防护,但不应被视为正式补丁的替代品。