首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 網頁程式設計 >> PHP 程式設計 >> PHP 綜合知識

PHP "Stack overflow" 錯誤徹底排查:Windows 目錄權限缺失導致的災難性後果

     編輯:PHP 綜合知識

錯誤現象

頁面頂部輸出:PHP has encountered a Stack overflow (PHP 遇到堆棧溢出)。
**關鍵特徵**:錯誤發生在**任何業務代碼執行前**,甚至 `echo 'test';` 都不會輸出。整個 PHP 進程啟動階段就崩潰。

根因:Windows 文件系統權限缺失導致無限遞歸

**核心原因**:PHP 處理腳本時需訪問**上級目錄** (如解析 `include_path`、解析相對路徑、讀取 `php.ini`、會話存儲目錄等)。若 **父目錄缺少「遍歷/列舉」權限** (Windows 稱「匿名來賓」/ `Users` / `IIS_IUSRS` 的 `Read & Execute` / `List folder / read data`),PHP 內部路徑解析函數會陷入**無限遞歸/重試循環**,最終耗盡 C 棧空間觸發 Stack Overflow。

經典場景複現

# 目錄結構
D:\www\jb51\webroot\    <-- DocumentRoot (網站根目錄)
D:\www\jb51\           <-- 父目錄

# 錯誤權限狀態
D:\www\jb51\webroot\   -> 用戶 IIS_IUSRS: 讀取/寫入/執行 ✅
D:\www\jb51\           -> 用戶 IIS_IUSRS: **無任何權限** ❌ (繼承被阻斷或顯式移除)

# PHP 請求流程
1. 請求 D:\www\jb51\webroot\index.php
2. PHP 內部需解析真實路徑 -> 遞歸向上查找 D:\www\jb51\
3. 訪問被拒 -> 內部錯誤處理 -> 重試/回溯 -> 無限遞歸 -> Stack Overflow

排查步驟 (一鍵定位)

  1. 檢查錯誤日誌:Windows 事件查看器 → Windows Logs → Application,源 `PHP` / `IIS`,事件 ID 通常包含 `Stack overflow`、`Access is denied` (0x5)。
  2. 對比正常環境權限:`icacls D:\www\jb51` vs `icacls D:\www\jb51\webroot`,重點看 `IIS_IUSRS`、`IUSR`、`Users`、`Authenticated Users` 是否有 `(RX)` (讀取/執行/列舉)。
  3. ProcMon 監控 (終極武器):
    • 過濾 `Process Name = php-cgi.exe` / `php.exe`
    • 看 `Result = ACCESS DENIED` 的 `Path`,特別是**父目錄**路徑。
    • 操作通常是 `QueryDirectory`、`CreateFile` (目錄)、`GetAttributes`。
  4. PHP 版本/模塊無關:更換 PHP 版本、禁用擴展**無法解決**,因為是進程啟動階段的文件系統訪問邏輯。

修復方案 (按推薦度)

方案一:補全父目錄權限 (標準修復)

# 命令行 (管理員權限)
# 給網站父目錄授予 IIS_IUSRS / IUSR 讀取/執行/列舉權限
icacls "D:\www\jb51" /grant "IIS_IUSRS:(OI)(CI)(RX)" /T
icacls "D:\www\jb51" /grant "IUSR:(OI)(CI)(RX)" /T

# 或圖形界面:右鍵屬性 -> 安全 -> 編輯 -> 添加 IIS_IUSRS / IUSR -> 勾選 "讀取和執行"、"列出文件夾內容"、"讀取"
  • `(OI)(CI)`:物件繼承/容器繼承 (遞歸應用到子文件/目錄)
  • `(RX)`:讀取/執行/列舉目錄 (關鍵:`List folder / read data`)

方案二:調整網站根目錄層級 (繞過方案、快速止血)

將 DocumentRoot 指向**權限正確的上級目錄**,或在權限正確的目錄下創建新項目目錄:

# 原結構 (父目錄無權限)
D:\www\jb51\webroot\  -> DocumentRoot

# 新結構 (在有權限的目錄下建站)
D:\www\jb51\new_site\ -> DocumentRoot (繼承 D:\www\jb51\ 權限)
# 或
D:\www\jb51\webroot\new_project\ -> DocumentRoot

**原理**:PHP 只會向上遍歷到 DocumentRoot 所在目錄,不會觸及更上級無權限目錄。

方案三:PHP 配置繞過 (治標不治本)

  • 設置 `open_basedir = D:\www\jb51\webroot` 限制 PHP 訪問範圍 (可能減少向上遍歷),但**不保證解決**,因為 PHP 內部路徑規範化可能仍需訪問父目錄元數據。
  • 設置 `doc_root` / `user_dir` (CGI 模式相關),風險較大。

為什麼是「Stack Overflow」而非「Permission Denied」?

PHP (Zend Engine) 路徑解析函數 (如 `VCWD_REALPATH`、`tsrm_realpath`、Windows 實現 `php_win32_check_trailing_space` 等) 在遇到 `ACCESS_DENIED` 時,**不直接返回錯誤**,而是嘗試替代路徑、標準化路徑、去除尾部空格/點等操作,導致**函數調用棧無限增長**,最終觸發 Windows 結構化異常處理 (SEH) 捕獲 `STATUS_STACK_OVERFLOW` (0xC00000FD),PHP 輸出 "Stack overflow" 並進程退出。

受影響的常見應用

  • Discuz! / PHPWind / ThinkPHP 等 PHP 框架項目
  • phpMyAdmin / Adminer 等管理工具
  • 任何部署在 **Windows + IIS / Apache (PHP-FPM/CGI)** 且**父目錄權限缺失**的 PHP 應用

預防清單 (部署前必檢)

# 1. 父目錄權限檢查腳本 (保存為 check_perms.php 放入網站根目錄訪問)
<?php
$root = realpath($_SERVER['DOCUMENT_ROOT']);
$parent = dirname($root);
echo "Web Root: $root\n";
echo "Parent:   $parent\n";
echo "Root readable: " . (is_readable($root) ? 'YES' : 'NO') . "\n";
echo "Parent readable: " . (is_readable($parent) ? 'YES' : 'NO') . "\n";
echo "Parent listable: " . (is_dir($parent) && ($h = @opendir($parent)) ? 'YES' : 'NO') . "\n";
if ($h) closedir($h);
?>

# 2. 部署腳本自動化權限設置 (PowerShell)
$webRoot = "D:\www\jb51\webroot"
$parent  = "D:\www\jb51"
$users = @("IIS_IUSRS", "IUSR")
foreach ($u in $users) {
    icacls $parent /grant "$u:(OI)(CI)(RX)" /T
    icacls $webRoot /grant "$u:(OI)(CI)(RX)" /T
}

# 3. 監控告警:Windows 事件日誌中出現 PHP Stack Overflow 立即告警

總結

Windows PHP 部署鐵律:**DocumentRoot 及其所有父目錄,必須授予運行 PHP 的用戶 (IIS_IUSRS/IUSR/Apache User) 至少 `Read & Execute` / `List folder / read data` 權限。**
這不是建議,是**生存條件**。Stack Overflow 是權限缺失在 Zend Engine 內部路徑解析遞歸中觸發的「心臟驟停」,而非業務代碼錯誤。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved