CVE-2026-109618 和 CVE-2026-109619的修复补丁解读
这份 diff 文件包含了针对 PHP 7.1 的两个高危漏洞(CVE-2026-109618和CVE-2026-109619)的修复补丁,以及相关的安全加固。
为了方便你阅读,我严格按照文件中的代码块,对**每一行发生变化的代码(以+或-开头)**进行了逐行中文注释。
1.ext/session/session.c(禁用原生上传进度)
-if(!PS(rfc1867_enabled)){+if(!PS(rfc1867_enabled)||1){/* Backport CVE-2026-109619 */- 注释:在原来的判断后强行加上
|| 1,使得这个条件永远为真。效果是直接return,彻底禁用了 PHP 原生的session.upload_progress(上传进度)功能。因为该功能涉及复杂的全局状态,官方在修复 CVE-2026-109619 时选择直接将其废弃/关闭以绝后患。
2.main/main.c(新增 multipart 限制与清理全局变量)
+PHP_INI_ENTRY("max_multipart_body_parts","-1",PHP_INI_SYSTEM|PHP_INI_PERDIR,NULL)- 注释:在 PHP 核心配置中注册一个新的 ini 选项
max_multipart_body_parts,默认值为-1。限制只能在系统级(SYSTEM)或目录级(PERDIR)修改,不能用ini_set动态修改。
for(i=0;i<NUM_TRACK_VARS;i++){zval_ptr_dtor(&PG(http_globals)[i]);+/* Backport CVE-2026-109619: pode ser acessado antes de php_request_startup() */+ZVAL_UNDEF(&PG(http_globals)[i]);}- 注释:在请求结束时,释放了超全局变量(如
$_GET,$_POST)的内存后,必须立刻将其标记为未定义(ZVAL_UNDEF)。防止下一个请求启动前有代码意外访问,造成“释放后使用”(UAF)漏洞。这段代码在文件里出现了两次,对应不同的退出清理路径。
3.main/rfc1867.c(收紧文件上传解析边界)
+staticchar*php_mime_get_hdr_value(zend_llist header,char*key);- 注释:前置声明一个辅助函数,用于检查某个 Header(头部)是否已经在链表中存在。
-do{value++;}while(isspace(*value));--key=estrdup(line);-smart_string_appends(&buf_value,value);+/* Backport: manter so cabecalhos usados, ignorar duplicados */+if((strcasecmp(line,"Content-Disposition")&&strcasecmp(line,"Content-Type"))+||php_mime_get_hdr_value(*header,line)){+key=NULL;+}else{+key=estrdup(line);+do{value++;}while(isspace((unsignedchar)*value));+smart_string_appends(&buf_value,value);+}- 注释:解析 Multipart 上传请求的 Header 时,只保留
Content-Disposition和Content-Type。如果遇到其他无关的 Header,或者遇到了重复的 Header(通过php_mime_get_hdr_value检查),直接把key置为NULL丢弃。这防止了攻击者利用畸形或海量 Header 消耗服务器资源。同时修复了isspace的入参强转类型,防止越界。
intupload_cnt=INI_INT("max_file_uploads");+intbody_parts_cnt=INI_INT("max_multipart_body_parts");+intbody_parts_limit;- 注释:读取刚才新增的
max_multipart_body_parts配置值,并声明限制变量。
+if(body_parts_cnt<0){+body_parts_cnt=PG(max_input_vars)+upload_cnt;+}+body_parts_limit=body_parts_cnt;- 注释:如果配置值为
-1(默认),则自动计算上限,等于max_input_vars(最大表单字段数)加上max_file_uploads(最大上传文件数)。并将计算后的值保存下来,用于报错提示。
+if(--body_parts_cnt<0){+php_error_docref(NULL,E_WARNING,"Multipart body parts limit exceeded %d. To increase the limit change max_multipart_body_parts in php.ini.",body_parts_limit);+gotofileupload_done;+}- 注释:在循环解析每个字段/文件前,计数器减一。如果超出限制(小于 0),抛出警告(
E_WARNING),然后通过goto直接跳到上传结束的代码块,丢弃后续内容。防止单次请求包含海量 part 造成 DoS 攻击。
4.main/SAPI.c(清理请求上下文指针)
if(SG(request_info).auth_user){efree(SG(request_info).auth_user);+SG(request_info).auth_user=NULL;}- 注释:在释放用户认证信息(
auth_user,auth_password,auth_digest)、content_type_dup、current_user的内存后,必须将指针置为NULL。防止野指针残留到下一个请求中(CVE-2026-109619 修复的一部分)。
5.Zend/zend_alloc.c(修复 OOM 时的致命错误处理)
+#include"zend_execute.h"- 注释:引入执行器头文件,因为下面新增的报错函数需要调用
zend_is_executing()等 API 来获取当前执行的文件名和行号。
-exit(1);+_exit(1);- 注释:将标准的
exit()替换为系统级_exit()。exit()会触发析构函数和缓冲区刷新;而_exit()会直接强杀进程,不执行任何清理。
+/* Backport CVE-2026-109618: fatal de memoria sem bailout; encerra o processo */+ZEND_API ZEND_COLD ZEND_NORETURNvoidzend_mm_safe_error_ex(constchar*format,...)+{+zend_string*script_filename=NULL;+uint32_tscript_lineno=0;+charbuf[1024];+va_list args;++if(zend_is_executing()){+script_filename=zend_get_executed_filename_ex();+script_lineno=zend_get_executed_lineno();+}elseif(zend_is_compiling()){+script_filename=zend_get_compiled_filename();+script_lineno=zend_get_compiled_lineno();+}++va_start(args,format);+vsnprintf(buf,sizeof(buf),format,args);+va_end(args);++fprintf(stderr,"\nFatal error: %s in %s on line %d\n",buf,+script_filename?ZSTR_VAL(script_filename):"Unknown",script_lineno);+#ifdef ZEND_WIN32+fflush(stderr);+#endif+_exit(1);}- 注释:新增了一个专门处理内存溢出的函数。它尝试获取报错的文件名和行号,格式化错误信息后,直接写入系统标准错误(
stderr),然后通过_exit(1)直接杀掉进程,不再像以前那样走正常的异常抛出(bailout)流程。
-heap->overflow=1;-zend_try{...}zend_catch{...}zend_end_try();-heap->overflow=0;-zend_bailout();-exit(1);+(void)heap;+zend_mm_safe_error_ex(format,...);- 注释:删除了旧版 OOM 处理的复杂逻辑。现在一旦发生 OOM,直接调用上面新增的
zend_mm_safe_error_ex函数,强杀进程。
fprintf(stderr,"Out of memory\n");-exit(1);+_exit(1);- 注释:同样针对系统级别真正的 OOM,将退出方式改为强杀(
_exit)。
6.Zend/zend_alloc.h(函数声明)
+ZEND_API ZEND_COLD ZEND_NORETURNvoidzend_mm_safe_error_ex(constchar*format,...);- 注释:在头文件中声明新增的 OOM 处理函数,以便其他 C 文件可以调用它。
7.Zend/zend_execute_API.c(清理执行器全局状态 - 核心修复)
+/* Backport CVE-2026-109619: estado que pode ser acessado antes de init_executor */+ZVAL_UNDEF(&EG(user_error_handler));+ZVAL_UNDEF(&EG(user_exception_handler));+memset(&EG(user_error_handlers_error_reporting),0,sizeof(EG(user_error_handlers_error_reporting)));+memset(&EG(user_error_handlers),0,sizeof(EG(user_error_handlers)));+memset(&EG(user_exception_handlers),0,sizeof(EG(user_exception_handlers)));+EG(objects_store).object_buckets=NULL;+EG(objects_store).top=1;+EG(error_handling)=EH_NORMAL;+EG(exception)=NULL;- 注释:这段是修复 CVE-2026-109619 的核心。在请求结束清理执行器(Executor)时,强制重置所有与错误处理、异常处理、对象池相关的全局状态。清空或置空这些变量,确保下一个请求(或者在异常退出后)不会读取到上一个请求残留的“脏数据”指针。
8.Zend/zend_llist.c(清理链表残留)
+l->head=NULL;+l->tail=NULL;l->count=0;- 注释:在清空双向链表时,除了将计数置为 0,还要把链表的头指针(head)和尾指针(tail)显式置为
NULL,防止悬挂指针。
地址:https://github.com/vperetti/php-cve-2026-109618-109619-backports
使用方法:https://blog.csdn.net/liuruiaaa/article/details/168773241