标签归档:php-cgi

[PHP] 5.3.3 上 php-cgi 处理完一个语法错误的请求后再接到请求立即崩溃

该问题已提交至 php 的 bug 库: http://bugs.php.net/bug.php?id=53022

1. 问题描述:
php-cgi 在以 FastCGI 方式工作时, 一个运行实例(进程)可以多次处理用户的请求, 实验发现, 当 php-cgi 运行一个语法解析出错的 php 脚本后, 再次尝试运行脚本则立即出现内存段错误导致程序崩溃.
从而导致有些时候明明 php-cgi 进程没死, 却有时在前台刷新时出现 gateway error(502), 但刷新过后又好了!

2. 试验方法:
1) 只启动一个子进程来启动 php-cgi (可能 php-fpm 也同样存在此问题,未测试)
在环境变量中设置 PHP_FCGI_CHILDREN=1

2) 编写一个明显语法错误的脚本,如:

3) 通过 web 访问这个脚本 2次以上,第一次正常运行,第二次开始则 php-cgi 崩溃然后 web 上提示 gateway error HTTP 502

4) gdb 追踪结果

Program received signal EXC_BAD_ACCESS, Could not access memory.
Reason: 13 at address: 0x0000000000000000
0x0000000100418211 in _zend_mm_alloc_int (heap=0x101025a00, size=4) at /Users/hightman/Temp/setup/php-5.3.3/Zend/zend_alloc.c:1825
1825 heap->cache[index] = best_fit->prev_free_block;
(gdb) bt
#0 0x0000000100418211 in _zend_mm_alloc_int (heap=0x101025a00, size=4) at /Users/hightman/Temp/setup/php-5.3.3/Zend/zend_alloc.c:1825
#1 0x00000001004196cc in _emalloc (size=4) at /Users/hightman/Temp/setup/php-5.3.3/Zend/zend_alloc.c:2340
#2 0x0000000100432af4 in init_op_array (op_array=0x100e5c2d0, type=2 ‘\002′, initial_ops_size=64) at /Users/hightman/Temp/setup/php-5.3.3/Zend/zend_opcode.c:63
#3 0x00000001003fa309 in compile_file (file_handle=0x7fff5fbfd660, type=8) at zend_language_scanner.l:351
#4 0x000000010043f8ee in zend_execute_scripts (type=8, retval=0x0, file_count=3) at /Users/hightman/Temp/setup/php-5.3.3/Zend/zend.c:1186
#5 0x00000001003c6e01 in php_execute_script (primary_file=0x7fff5fbfd660) at /Users/hightman/Temp/setup/php-5.3.3/main/main.c:2260
#6 0x00000001005222f3 in main (argc=3, argv=0x7fff5fbff850) at /Users/hightman/Temp/setup/php-5.3.3/sapi/cgi/cgi_main.c:2109

3. 解决方案:
1) 经过几个小时的追踪和努力,终于暂时解决了这个BUG,但尚不清楚是否完整彻底的消除了这个BUG,正等待 PHP 开发组的专家确诊。
2) 按下面的 diff 输出文件的方式修改 zend/zend_language_scanner.l

*** zend/zend_language_scanner.l.orig 2010-10-08 20:48:35.000000000 +0800
— zend/zend_language_scanner.l 2010-10-08 20:49:36.000000000 +0800
***************
*** 355,360 ****
— 355,361 —-
zend_do_return(&retval_znode, 0 TSRMLS_CC);
CG(in_compilation) = original_in_compilation;
if (compiler_result==1) { /* parser error */
+ zend_restore_lexical_state(&original_lex_state TSRMLS_CC);
zend_bailout();
}
compilation_successful=1;

3) 重新编译 php, 正常情况它会自动重新生成 zend/zend_language_scanner.c
前提是你的系统上安装了 re2c 这个工具, 如果没有安装,可以参考上在的 patch 同样手动直接修改 zend/zend_language_scanner.c 。

2) Re-compile the php, and it will generate a new zend/zend_language_scanner.c automatically, but you should install `re2c` first. Otherwise, you should modify zend/zend_language_scanner.c refers to the above patch.