補(bǔ)丁解讀)
這份 diff 文件包含了針對(duì) PHP 7.1 的兩個(gè)高危漏洞CVE-2026-109618和CVE-2026-109619的修復(fù)補(bǔ)丁以及相關(guān)的安全加固。為了方便你閱讀我嚴(yán)格按照文件中的代碼塊對(duì)**每一行發(fā)生變化的代碼以或-開(kāi)頭**進(jìn)行了逐行中文注釋。1.ext/session/session.c(禁用原生上傳進(jìn)度)-if(!PS(rfc1867_enabled)){if(!PS(rfc1867_enabled)||1){/* Backport CVE-2026-109619 */注釋在原來(lái)的判斷后強(qiáng)行加上|| 1使得這個(gè)條件永遠(yuǎn)為真。效果是直接return徹底禁用了 PHP 原生的session.upload_progress上傳進(jìn)度功能。因?yàn)樵摴δ苌婕皬?fù)雜的全局狀態(tài)官方在修復(fù) CVE-2026-109619 時(shí)選擇直接將其廢棄/關(guān)閉以絕后患。2.main/main.c(新增 multipart 限制與清理全局變量)PHP_INI_ENTRY(max_multipart_body_parts,-1,PHP_INI_SYSTEM|PHP_INI_PERDIR,NULL)注釋在 PHP 核心配置中注冊(cè)一個(gè)新的 ini 選項(xiàng)max_multipart_body_parts默認(rèn)值為-1。限制只能在系統(tǒng)級(jí)SYSTEM或目錄級(jí)PERDIR修改不能用ini_set動(dòng)態(tài)修改。for(i0;iNUM_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]);}注釋在請(qǐng)求結(jié)束時(shí)釋放了超全局變量如$_GET,$_POST的內(nèi)存后必須立刻將其標(biāo)記為未定義ZVAL_UNDEF。防止下一個(gè)請(qǐng)求啟動(dòng)前有代碼意外訪問(wèn)造成“釋放后使用”UAF漏洞。這段代碼在文件里出現(xiàn)了兩次對(duì)應(yīng)不同的退出清理路徑。3.main/rfc1867.c(收緊文件上傳解析邊界)staticchar*php_mime_get_hdr_value(zend_llist header,char*key);注釋前置聲明一個(gè)輔助函數(shù)用于檢查某個(gè) Header頭部是否已經(jīng)在鏈表中存在。-do{value;}while(isspace(*value));--keyestrdup(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)){keyNULL;}else{keyestrdup(line);do{value;}while(isspace((unsignedchar)*value));smart_string_appends(buf_value,value);}注釋解析 Multipart 上傳請(qǐng)求的 Header 時(shí)只保留Content-Disposition和Content-Type。如果遇到其他無(wú)關(guān)的 Header或者遇到了重復(fù)的 Header通過(guò)php_mime_get_hdr_value檢查直接把key置為NULL丟棄。這防止了攻擊者利用畸形或海量 Header 消耗服務(wù)器資源。同時(shí)修復(fù)了isspace的入?yún)?qiáng)轉(zhuǎn)類(lèi)型防止越界。intupload_cntINI_INT(max_file_uploads);intbody_parts_cntINI_INT(max_multipart_body_parts);intbody_parts_limit;注釋讀取剛才新增的max_multipart_body_parts配置值并聲明限制變量。if(body_parts_cnt0){body_parts_cntPG(max_input_vars)upload_cnt;}body_parts_limitbody_parts_cnt;注釋如果配置值為-1默認(rèn)則自動(dòng)計(jì)算上限等于max_input_vars最大表單字段數(shù)加上max_file_uploads最大上傳文件數(shù)。并將計(jì)算后的值保存下來(lái)用于報(bào)錯(cuò)提示。if(--body_parts_cnt0){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;}注釋在循環(huán)解析每個(gè)字段/文件前計(jì)數(shù)器減一。如果超出限制小于 0拋出警告E_WARNING然后通過(guò)goto直接跳到上傳結(jié)束的代碼塊丟棄后續(xù)內(nèi)容。防止單次請(qǐng)求包含海量 part 造成 DoS 攻擊。4.main/SAPI.c(清理請(qǐng)求上下文指針)if(SG(request_info).auth_user){efree(SG(request_info).auth_user);SG(request_info).auth_userNULL;}注釋在釋放用戶(hù)認(rèn)證信息auth_user,auth_password,auth_digest、content_type_dup、current_user的內(nèi)存后必須將指針置為NULL。防止野指針殘留到下一個(gè)請(qǐng)求中CVE-2026-109619 修復(fù)的一部分。5.Zend/zend_alloc.c(修復(fù) OOM 時(shí)的致命錯(cuò)誤處理)#includezend_execute.h注釋引入執(zhí)行器頭文件因?yàn)橄旅嫘略龅膱?bào)錯(cuò)函數(shù)需要調(diào)用zend_is_executing()等 API 來(lái)獲取當(dāng)前執(zhí)行的文件名和行號(hào)。-exit(1);_exit(1);注釋將標(biāo)準(zhǔn)的exit()替換為系統(tǒng)級(jí)_exit()。exit()會(huì)觸發(fā)析構(gòu)函數(shù)和緩沖區(qū)刷新而_exit()會(huì)直接強(qiáng)殺進(jìn)程不執(zhí)行任何清理。/* 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_filenameNULL;uint32_tscript_lineno0;charbuf[1024];va_list args;if(zend_is_executing()){script_filenamezend_get_executed_filename_ex();script_linenozend_get_executed_lineno();}elseif(zend_is_compiling()){script_filenamezend_get_compiled_filename();script_linenozend_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_WIN32fflush(stderr);#endif_exit(1);}注釋新增了一個(gè)專(zhuān)門(mén)處理內(nèi)存溢出的函數(shù)。它嘗試獲取報(bào)錯(cuò)的文件名和行號(hào)格式化錯(cuò)誤信息后直接寫(xiě)入系統(tǒng)標(biāo)準(zhǔn)錯(cuò)誤stderr然后通過(guò)_exit(1)直接殺掉進(jìn)程不再像以前那樣走正常的異常拋出bailout流程。-heap-overflow1;-zend_try{...}zend_catch{...}zend_end_try();-heap-overflow0;-zend_bailout();-exit(1);(void)heap;zend_mm_safe_error_ex(format,...);注釋刪除了舊版 OOM 處理的復(fù)雜邏輯?,F(xiàn)在一旦發(fā)生 OOM直接調(diào)用上面新增的zend_mm_safe_error_ex函數(shù)強(qiáng)殺進(jìn)程。fprintf(stderr,Out of memory\n);-exit(1);_exit(1);注釋同樣針對(duì)系統(tǒng)級(jí)別真正的 OOM將退出方式改為強(qiáng)殺_exit。6.Zend/zend_alloc.h(函數(shù)聲明)ZEND_API ZEND_COLD ZEND_NORETURNvoidzend_mm_safe_error_ex(constchar*format,...);注釋在頭文件中聲明新增的 OOM 處理函數(shù)以便其他 C 文件可以調(diào)用它。7.Zend/zend_execute_API.c(清理執(zhí)行器全局狀態(tài) - 核心修復(fù))/* 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_bucketsNULL;EG(objects_store).top1;EG(error_handling)EH_NORMAL;EG(exception)NULL;注釋這段是修復(fù) CVE-2026-109619 的核心。在請(qǐng)求結(jié)束清理執(zhí)行器Executor時(shí)強(qiáng)制重置所有與錯(cuò)誤處理、異常處理、對(duì)象池相關(guān)的全局狀態(tài)。清空或置空這些變量確保下一個(gè)請(qǐng)求或者在異常退出后不會(huì)讀取到上一個(gè)請(qǐng)求殘留的“臟數(shù)據(jù)”指針。8.Zend/zend_llist.c(清理鏈表殘留)l-headNULL;l-tailNULL;l-count0;注釋在清空雙向鏈表時(shí)除了將計(jì)數(shù)置為 0還要把鏈表的頭指針head和尾指針tail顯式置為NULL防止懸掛指針。地址https://github.com/vperetti/php-cve-2026-109618-109619-backports使用方法https://blog.csdn.net/liuruiaaa/article/details/168773241