php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Bug #51329 call_user_func_array() crashes with autoload in recursive calls
Submitted: 2010-03-19 10:29 UTC Modified: 2012-04-28 07:26 UTC
Votes:4
Avg. Score:5.0 ± 0.0
Reproduced:4 of 4 (100.0%)
Same Version:2 (50.0%)
Same OS:1 (25.0%)
From: gergely dot fabian at radix-technologies dot com Assigned:
Status: Wont fix Package: Reproducible crash
PHP Version: 5.2.13 OS: Ubuntu
Private report: No CVE-ID: None
View Add Comment Developer Edit
Welcome! If you don't have a Git account, you can't do anything here.
You can add a comment by following this link or if you reported this bug, you can edit this bug over here.
(description)
Block user comment
Status: Assign to:
Package:
Bug Type:
Summary:
From: gergely dot fabian at radix-technologies dot com
New email:
PHP Version: OS:

 

 [2010-03-19 10:29 UTC] gergely dot fabian at radix-technologies dot com
Description:
------------
PHP 5.2.13 (cli) (built: Mar 19 2010 09:37:12) (configured with ./configure --prefix=$HOME --enable-debug)
2.6.31-20-generic #58-Ubuntu SMP Fri Mar 12 04:38:19 UTC 2010 x86_64 GNU/Linux

Calling class methods with call_user_func_array() in a recursive call-chain - if call_user_func_array() autoloads the subsequent class on each step - will cause a Segmentation fault (if the recursion is enough deep).

If I run the sample script then I'll get a Segmentation fault at 31st call.
This was reproducible both on PHP 5.2.13 and 5.2.10-2ubuntu6.4.
There is no segfault though if call_user_func_array() is preceded by a call to class_exists().

Test script:
---------------
$limit = 50;
for($i = 1; $i <= $limit; $i++){
  $f = "<?php
echo \"P$i loaded\\n\";
class P$i{
  public static function execute_me(){
    return \"P$i executed\\n\";
  }
}";
  file_put_contents("P$i.class.php", $f);
}
function __autoload($class_name) {
    require_once $class_name . '.class.php';
}
function callback($limit, $i = 1){
  //class_exists("P$i");
  echo call_user_func_array(array("P$i", 'execute_me'), array());
  if($i < $limit) callback($limit, $i+1);
}
callback($limit);

Expected result:
----------------
P1 loaded
P1 executed
P2 loaded
P2 executed
P3 loaded
P3 executed
...
...
P50 loaded
P50 executed

Actual result:
--------------
P1 loaded
P1 executed
P2 loaded
P2 executed
P3 loaded
P3 executed
...
...
P30 loaded
P30 executed
P31 loaded
Segmentation fault (writing memory)

This is the gdb backtrace:

Core was generated by `php call_user_func_array_bug.php'.
Program terminated with signal 11, Segmentation fault.
#0  0x0000000000586ee9 in zif_call_user_func_array (ht=<value optimized out>, return_value=0x1d5adf8, return_value_ptr=<value optimized out>, 
    this_ptr=<value optimized out>, return_value_used=<value optimized out>) at /home/user/opt/php-5.2.13/ext/standard/basic_functions.c:5201
5201		func_params_ht = Z_ARRVAL_PP(params);
(gdb) bt
#0  0x0000000000586ee9 in zif_call_user_func_array (ht=<value optimized out>, return_value=0x1d5adf8, return_value_ptr=<value optimized out>, 
    this_ptr=<value optimized out>, return_value_used=<value optimized out>) at /home/user/opt/php-5.2.13/ext/standard/basic_functions.c:5201
#1  0x00000000006526b0 in zend_do_fcall_common_helper_SPEC (execute_data=0x7fff1e2611b0) at /home/user/opt/php-5.2.13/Zend/zend_vm_execute.h:200
#2  0x000000000064df04 in execute (op_array=0x1d414a0) at /home/user/opt/php-5.2.13/Zend/zend_vm_execute.h:92
#3  0x0000000000652056 in zend_do_fcall_common_helper_SPEC (execute_data=0x7fff1e261490) at /home/user/opt/php-5.2.13/Zend/zend_vm_execute.h:234
#4  0x000000000064df04 in execute (op_array=0x1d414a0) at /home/user/opt/php-5.2.13/Zend/zend_vm_execute.h:92
#5  0x0000000000652056 in zend_do_fcall_common_helper_SPEC (execute_data=0x7fff1e261770) at /home/user/opt/php-5.2.13/Zend/zend_vm_execute.h:234
#6  0x000000000064df04 in execute (op_array=0x1d414a0) at /home/user/opt/php-5.2.13/Zend/zend_vm_execute.h:92
#7  0x0000000000652056 in zend_do_fcall_common_helper_SPEC (execute_data=0x7fff1e261a50) at /home/user/opt/php-5.2.13/Zend/zend_vm_execute.h:234
#8  0x000000000064df04 in execute (op_array=0x1d414a0) at /home/user/opt/php-5.2.13/Zend/zend_vm_execute.h:92
#9  0x0000000000652056 in zend_do_fcall_common_helper_SPEC (execute_data=0x7fff1e261d30) at /home/user/opt/php-5.2.13/Zend/zend_vm_execute.h:234
#10 0x000000000064df04 in execute (op_array=0x1d414a0) at /home/user/opt/php-5.2.13/Zend/zend_vm_execute.h:92
#11 0x0000000000652056 in zend_do_fcall_common_helper_SPEC (execute_data=0x7fff1e262010) at /home/user/opt/php-5.2.13/Zend/zend_vm_execute.h:234
#12 0x000000000064df04 in execute (op_array=0x1d414a0) at /home/user/opt/php-5.2.13/Zend/zend_vm_execute.h:92
#13 0x0000000000652056 in zend_do_fcall_common_helper_SPEC (execute_data=0x7fff1e2622f0) at /home/user/opt/php-5.2.13/Zend/zend_vm_execute.h:234
#14 0x000000000064df04 in execute (op_array=0x1d414a0) at /home/user/opt/php-5.2.13/Zend/zend_vm_execute.h:92
#15 0x0000000000652056 in zend_do_fcall_common_helper_SPEC (execute_data=0x7fff1e2625d0) at /home/user/opt/php-5.2.13/Zend/zend_vm_execute.h:234
#16 0x000000000064df04 in execute (op_array=0x1d414a0) at /home/user/opt/php-5.2.13/Zend/zend_vm_execute.h:92
#17 0x0000000000652056 in zend_do_fcall_common_helper_SPEC (execute_data=0x7fff1e2628b0) at /home/user/opt/php-5.2.13/Zend/zend_vm_execute.h:234
#18 0x000000000064df04 in execute (op_array=0x1d414a0) at /home/user/opt/php-5.2.13/Zend/zend_vm_execute.h:92
#19 0x0000000000652056 in zend_do_fcall_common_helper_SPEC (execute_data=0x7fff1e262b90) at /home/user/opt/php-5.2.13/Zend/zend_vm_execute.h:234
#20 0x000000000064df04 in execute (op_array=0x1d414a0) at /home/user/opt/php-5.2.13/Zend/zend_vm_execute.h:92
#21 0x0000000000652056 in zend_do_fcall_common_helper_SPEC (execute_data=0x7fff1e262e70) at /home/user/opt/php-5.2.13/Zend/zend_vm_execute.h:234
#22 0x000000000064df04 in execute (op_array=0x1d414a0) at /home/user/opt/php-5.2.13/Zend/zend_vm_execute.h:92
#23 0x0000000000652056 in zend_do_fcall_common_helper_SPEC (execute_data=0x7fff1e263150) at /home/user/opt/php-5.2.13/Zend/zend_vm_execute.h:234
#24 0x000000000064df04 in execute (op_array=0x1d414a0) at /home/user/opt/php-5.2.13/Zend/zend_vm_execute.h:92
#25 0x0000000000652056 in zend_do_fcall_common_helper_SPEC (execute_data=0x7fff1e263430) at /home/user/opt/php-5.2.13/Zend/zend_vm_execute.h:234
#26 0x000000000064df04 in execute (op_array=0x1d414a0) at /home/user/opt/php-5.2.13/Zend/zend_vm_execute.h:92
#27 0x0000000000652056 in zend_do_fcall_common_helper_SPEC (execute_data=0x7fff1e263710) at /home/user/opt/php-5.2.13/Zend/zend_vm_execute.h:234
#28 0x000000000064df04 in execute (op_array=0x1d414a0) at /home/user/opt/php-5.2.13/Zend/zend_vm_execute.h:92
#29 0x0000000000652056 in zend_do_fcall_common_helper_SPEC (execute_data=0x7fff1e2639f0) at /home/user/opt/php-5.2.13/Zend/zend_vm_execute.h:234
#30 0x000000000064df04 in execute (op_array=0x1d414a0) at /home/user/opt/php-5.2.13/Zend/zend_vm_execute.h:92
#31 0x0000000000652056 in zend_do_fcall_common_helper_SPEC (execute_data=0x7fff1e263cd0) at /home/user/opt/php-5.2.13/Zend/zend_vm_execute.h:234
#32 0x000000000064df04 in execute (op_array=0x1d414a0) at /home/user/opt/php-5.2.13/Zend/zend_vm_execute.h:92
#33 0x0000000000652056 in zend_do_fcall_common_helper_SPEC (execute_data=0x7fff1e263fb0) at /home/user/opt/php-5.2.13/Zend/zend_vm_execute.h:234
#34 0x000000000064df04 in execute (op_array=0x1d414a0) at /home/user/opt/php-5.2.13/Zend/zend_vm_execute.h:92
#35 0x0000000000652056 in zend_do_fcall_common_helper_SPEC (execute_data=0x7fff1e264290) at /home/user/opt/php-5.2.13/Zend/zend_vm_execute.h:234
#36 0x000000000064df04 in execute (op_array=0x1d414a0) at /home/user/opt/php-5.2.13/Zend/zend_vm_execute.h:92
#37 0x0000000000652056 in zend_do_fcall_common_helper_SPEC (execute_data=0x7fff1e264570) at /home/user/opt/php-5.2.13/Zend/zend_vm_execute.h:234
#38 0x000000000064df04 in execute (op_array=0x1d414a0) at /home/user/opt/php-5.2.13/Zend/zend_vm_execute.h:92
#39 0x0000000000652056 in zend_do_fcall_common_helper_SPEC (execute_data=0x7fff1e264850) at /home/user/opt/php-5.2.13/Zend/zend_vm_execute.h:234
#40 0x000000000064df04 in execute (op_array=0x1d414a0) at /home/user/opt/php-5.2.13/Zend/zend_vm_execute.h:92
#41 0x0000000000652056 in zend_do_fcall_common_helper_SPEC (execute_data=0x7fff1e264b30) at /home/user/opt/php-5.2.13/Zend/zend_vm_execute.h:234
#42 0x000000000064df04 in execute (op_array=0x1d414a0) at /home/user/opt/php-5.2.13/Zend/zend_vm_execute.h:92
#43 0x0000000000652056 in zend_do_fcall_common_helper_SPEC (execute_data=0x7fff1e264e10) at /home/user/opt/php-5.2.13/Zend/zend_vm_execute.h:234
#44 0x000000000064df04 in execute (op_array=0x1d414a0) at /home/user/opt/php-5.2.13/Zend/zend_vm_execute.h:92
#45 0x0000000000652056 in zend_do_fcall_common_helper_SPEC (execute_data=0x7fff1e2650f0) at /home/user/opt/php-5.2.13/Zend/zend_vm_execute.h:234
#46 0x000000000064df04 in execute (op_array=0x1d414a0) at /home/user/opt/php-5.2.13/Zend/zend_vm_execute.h:92
#47 0x0000000000652056 in zend_do_fcall_common_helper_SPEC (execute_data=0x7fff1e2653d0) at /home/user/opt/php-5.2.13/Zend/zend_vm_execute.h:234
#48 0x000000000064df04 in execute (op_array=0x1d414a0) at /home/user/opt/php-5.2.13/Zend/zend_vm_execute.h:92
#49 0x0000000000652056 in zend_do_fcall_common_helper_SPEC (execute_data=0x7fff1e2656b0) at /home/user/opt/php-5.2.13/Zend/zend_vm_execute.h:234
#50 0x000000000064df04 in execute (op_array=0x1d414a0) at /home/user/opt/php-5.2.13/Zend/zend_vm_execute.h:92
#51 0x0000000000652056 in zend_do_fcall_common_helper_SPEC (execute_data=0x7fff1e265990) at /home/user/opt/php-5.2.13/Zend/zend_vm_execute.h:234
#52 0x000000000064df04 in execute (op_array=0x1d414a0) at /home/user/opt/php-5.2.13/Zend/zend_vm_execute.h:92
#53 0x0000000000652056 in zend_do_fcall_common_helper_SPEC (execute_data=0x7fff1e265c70) at /home/user/opt/php-5.2.13/Zend/zend_vm_execute.h:234
#54 0x000000000064df04 in execute (op_array=0x1d414a0) at /home/user/opt/php-5.2.13/Zend/zend_vm_execute.h:92
#55 0x0000000000652056 in zend_do_fcall_common_helper_SPEC (execute_data=0x7fff1e265f50) at /home/user/opt/php-5.2.13/Zend/zend_vm_execute.h:234
#56 0x000000000064df04 in execute (op_array=0x1d414a0) at /home/user/opt/php-5.2.13/Zend/zend_vm_execute.h:92
#57 0x0000000000652056 in zend_do_fcall_common_helper_SPEC (execute_data=0x7fff1e266230) at /home/user/opt/php-5.2.13/Zend/zend_vm_execute.h:234
#58 0x000000000064df04 in execute (op_array=0x1d414a0) at /home/user/opt/php-5.2.13/Zend/zend_vm_execute.h:92
#59 0x0000000000652056 in zend_do_fcall_common_helper_SPEC (execute_data=0x7fff1e266510) at /home/user/opt/php-5.2.13/Zend/zend_vm_execute.h:234
#60 0x000000000064df04 in execute (op_array=0x1d414a0) at /home/user/opt/php-5.2.13/Zend/zend_vm_execute.h:92
#61 0x0000000000652056 in zend_do_fcall_common_helper_SPEC (execute_data=0x7fff1e2667f0) at /home/user/opt/php-5.2.13/Zend/zend_vm_execute.h:234
#62 0x000000000064df04 in execute (op_array=0x1d414a0) at /home/user/opt/php-5.2.13/Zend/zend_vm_execute.h:92
#63 0x0000000000652056 in zend_do_fcall_common_helper_SPEC (execute_data=0x7fff1e266b30) at /home/user/opt/php-5.2.13/Zend/zend_vm_execute.h:234
#64 0x000000000064df04 in execute (op_array=0x1d12c20) at /home/user/opt/php-5.2.13/Zend/zend_vm_execute.h:92
#65 0x000000000062b96a in zend_execute_scripts (type=<value optimized out>, retval=0x0, file_count=3) at /home/user/opt/php-5.2.13/Zend/zend.c:1134
#66 0x00000000005ea9d3 in php_execute_script (primary_file=<value optimized out>) at /home/user/opt/php-5.2.13/main/main.c:2036
#67 0x000000000069a1e7 in main (argc=<value optimized out>, argv=<value optimized out>) at /home/user/opt/php-5.2.13/sapi/cli/php_cli.c:1165

Patches

bug51329.patch (last revision 2012-04-28 07:25 UTC by laruence@php.net)

Add a Patch

Pull Requests

Add a Pull Request

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2010-03-19 11:38 UTC] gergely dot fabian at radix-technologies dot com
I managed to reproduce this bug (I guess it's the same) in another way, which is a lot closer to our original test case (that was about having a given number of calls in the call stack causing later autoload of call_user_func_array() to fail, but if we had more number of calls in call stack, then there was no segfault).
This code will have a segfault in the 25th call (it mainly depends on the number of parameters given to "callback"). If we miss calling call_user_func_array() in the 25th recursion (and simply make another recursion) then later call_user_func_array() calls will be ok. I guess making a simple recursion at 25th steps mask (or fixes) the bug in the call stack.

<?php
$limit = 25; // till when to autoload classes
// change $limit2 to 25, and P25's autoload will be missed (and an additional callback will be called before next autoloading)
// then there is no segmentation fault, and it runs till 50
$limit2 = 24; // re-enable autoloading after this limit
$limit3 = 50; // iterate till this
for($i = 1; $i <= $limit3; $i++){
  $f = "<?php
echo \"P$i loaded\\n\";
class P$i{
  public static function execute_me(){
    return \"P$i executed\\n\";
  }
}";
  file_put_contents("P$i.class.php", $f);
}
function __autoload($class_name) {
    require_once $class_name . '.class.php';
}
function callback($limit, $limit2, $i = 1){
  global $limit3;
  echo "Calling callback $i\n";
  if ($i < $limit || $i > $limit2)  // enable some $i where we won't autoload
  {
    //class_exists("P$i");
    echo call_user_func_array(array("P$i", 'execute_me'), array());
  }
  if ($i < $limit3) callback($limit, $limit2, $i+1);
}
callback($limit, $limit2);
 [2010-03-21 19:24 UTC] johannes@php.net
-Status: Open +Status: Bogus
 [2010-03-21 19:24 UTC] johannes@php.net
Infinite recursion is known to segfault and expected behavior. See other reports about it.
 [2010-03-21 21:59 UTC] gergely dot fabian at radix-technologies dot com
This is not infinite recursion (is 31/25 recursion too deep?).
See my comment with putting one element to call stack "fixing" later autoloading (2010-03-19 09:38 UTC).
This is an autoloading/call_user_func_array bug imho. Otherwise if it would be normal for this to happen, why does class_exists() call right before the segfaulting place fix it?
 [2010-03-22 07:24 UTC] gergely dot fabian at radix-technologies dot com
The infinite recursion that you mentioned also causes segfault, but it's another case. If I remove the call_user_func_array call, and the limit of 50, then it will segfault after around 14860 recursive calls (on my machine).
My original code (and the second version I posted) segfaults at 25/31st recursion. That's a different amount I'd say.
If I change the second version of my testcase to jump call_user_func_array call at 25th recursion, then it runs until 208th recursion and dies again on call_user_func_array.
If enable though class_exists() before call_user_func_array(), then recursion is successful for both 25th and 208th.
 [2010-03-22 07:40 UTC] gergely dot fabian at radix-technologies dot com
I have to emphasize that the use case where this bug came out was not infinite recursion. 
It is in an MVC web application framework (symfony) that a given amount of filters (let's say 15) are one-by-one called by the filterChain's execute() method, and then call back to filterChain->execute() (thus making a sort of indirect recursion).
In our case if we have certain conditions true and have a 7th filter, then a later call_user_func_array call will cause a segfault. Having less number of filters (disabling any of them) "fixes" the bug (not reaching that amount of recursion), as also having one more filter (making an additional recursion step).
The second testcase reproduces this with plain PHP code.
 [2010-07-09 08:34 UTC] denis at bitrix dot ru
I have the same problem. Why is it bogus? There is no unlimit recursion!
The only workaround I found is not to use autoload.
 [2010-07-09 09:48 UTC] ivo at danihelka dot net
I simplified the test case.
It is not needed to create the classes on disk.

Test script:
------------
$limit = 50;

function __autoload($class_name) {
    eval("
echo \"$class_name loaded\\n\";
class $class_name {
  public static function execute_me(){
    return \"$class_name executed\\n\";
  }
}");
}

function go_deeper($limit, $i = 1){
  echo call_user_func_array(array("P$i", 'execute_me'), array());
  if($i < $limit) go_deeper($limit, $i+1);
}

go_deeper($limit);
echo "Success\n";
 [2012-04-28 07:25 UTC] laruence@php.net
The following patch has been added/updated:

Patch Name: bug51329.patch
Revision:   1335597915
URL:        https://bugs.php.net/patch-display.php?bug=51329&patch=bug51329.patch&revision=1335597915
 [2012-04-28 07:26 UTC] laruence@php.net
-Status: Not a bug +Status: Wont fix
 [2012-04-28 07:26 UTC] laruence@php.net
Since 5.2 is not supported anymore,  so I only commit the patch here. 
You should always upgrade to a decent version of PHP :)
 
PHP Copyright © 2001-2021 The PHP Group
All rights reserved.
Last updated: Sat Jan 23 15:01:23 2021 UTC