php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Bug #64987 unexpected result for call_user_func() in the debug_backtrace()
Submitted: 2013-06-07 10:48 UTC Modified: 2017-10-24 08:30 UTC
Votes:3
Avg. Score:3.3 ± 1.7
Reproduced:2 of 2 (100.0%)
Same Version:1 (50.0%)
Same OS:2 (100.0%)
From: tyrael@php.net Assigned: requinix (profile)
Status: Closed Package: Scripting Engine problem
PHP Version: 5.3.26 OS: irrelevant
Private report: No CVE-ID: None
 [2013-06-07 10:48 UTC] tyrael@php.net
Description:
------------
call_user_func() generates two entry to the backtrace: one for the call of the 
call_user_func with the callable arg and one for the call of the callable.
the problem is that the entry only has function and args entry, but no file or 
line.
This happens because the execution reaches the break here:
http://lxr.php.net/xref/PHP_5_3/Zend/zend_builtin_functions.c#2161
Which based on the comment above is there to prevent touching the stack when we 
are inside the error handler, which isn't the case here.

When discussing this with Nikita on irc he said that we shouldn't have two entry 
in the result in the first place for call_user_func, but I think that removing 
one entry would have bigger impact on userland (there is a chance that some 
people already remove the entry manually and this change would make it remove o 
valid entry) compared to the change to fill out the information that was ommited 
before.

btw it seems that the suggested behavior was present between 5.0.0 and 5.0.5:
http://3v4l.org/jI9VI#v430
5.0.5 had two debug_backtrace related fix mentioned in the changelog(#30828 and 
#28377) but from a quick glance on the description it seems to be irrelevant 
from this behavior change.

In case if we decide to not change the current behavior, please turn this report 
into a documentation problem as it would be nice if the docs would reflect what 
information will present (or missing) in which case.
Currently we only hint that the type and args can be missing in some case.

ps: the issue is still present in master and commenting out the if with that 
break produces the expected result but ofc. that isn't the proper fix.

Test script:
---------------
<?php
function foo() {
        var_dump(bar());
}
function bar() {
        return debug_backtrace();
}
call_user_func("foo");

Expected result:
----------------
array(3) {
  [0]=>
  array(4) {
    ["file"]=>
    string(9) "/in/jI9VI"
    ["line"]=>
    int(3)
    ["function"]=>
    string(3) "bar"
    ["args"]=>
    array(0) {
    }
  }
  [1]=>
  array(4) {
    ["file"]=>
    string(9) "/in/jI9VI"
    ["line"]=>
    int(8)
    ["function"]=>
    string(3) "foo"
    ["args"]=>
    array(0) {
    }
  }
  [2]=>
  array(4) {
    ["file"]=>
    string(9) "/in/jI9VI"
    ["line"]=>
    int(8)
    ["function"]=>
    string(14) "call_user_func"
    ["args"]=>
    array(1) {
      [0]=>
      &string(3) "foo"
    }
  }
}

Actual result:
--------------
array(3) {
  [0]=>
  array(4) {
    ["file"]=>
    string(9) "/in/jI9VI"
    ["line"]=>
    int(3)
    ["function"]=>
    string(3) "bar"
    ["args"]=>
    array(0) {
    }
  }
  [1]=>
  array(2) {
    ["function"]=>
    string(3) "foo"
    ["args"]=>
    array(0) {
    }
  }
  [2]=>
  array(4) {
    ["file"]=>
    string(9) "/in/jI9VI"
    ["line"]=>
    int(8)
    ["function"]=>
    string(14) "call_user_func"
    ["args"]=>
    array(1) {
      [0]=>
      &string(3) "foo"
    }
  }
}

Patches

add_call_proxy_flag.patch (last revision 2013-06-09 09:00 UTC by laruence@php.net)

Add a Patch

Pull Requests

Add a Pull Request

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2013-06-07 10:52 UTC] tyrael@php.net
sorry, I've just realized that we had a bug report about this(#44428) closed by 
Dmitry with not a bug.
I still think that it would worth a second look, we either say that this is an 
internal call and then it shouldn't be in the debug_backtrace output or we say 
that this is useful information to the userland and have it in the backtrace, but 
then we can't have the internal function excuse.
 [2013-06-07 11:04 UTC] dmitry@php.net
-Status: Open +Status: Assigned -Assigned To: +Assigned To: laruence
 [2013-06-07 11:25 UTC] tyrael@php.net
it seems that the xdebug debug backtrace works the same way as it was proposed 
here:

Call Stack:
    0.0016     639496   1. {main}() test.php:0
    0.0026     639624   2. call_user_func() test.php:9
    0.0026     639624   3. foo() test.php:9
    0.0026     639624   4. bar() test.php:3
    0.0026     639800   5. trigger_error() test.php:7

notice that it lists both the call_user_func() and the foo() call, both of the 
pointing to the same file:line
 [2013-06-07 12:43 UTC] nikic@php.net
> When discussing this with Nikita on irc he said that we shouldn't have
> two entry in the result in the first place for call_user_func, but I think
> that removing one entry would have bigger impact on userland (there is a
> chance that some people already remove the entry manually and this change
> would make it remove o valid entry) compared to the change to fill out the
> information that was ommited before.

Misunderstanding ^^ I think having two entries is right (after all, both functions *are* called, so they should both be in the trace). But I don't think that the foo() call should copy the file&line info from the call_user_func() call. It's a) redundant and b) inadequate, as the foo() call does *not* happen in that line, but rather somewhere in the internals of call_user_func.

Now, for call_user_func in particular that distinction might be a bit fuzzy, as call_user_func($foo) is roughly equivalent to $foo(), but what you say here applies to all cases where a userland function is invoked from internal code. If you just copied the file&line from the previous frame in those cases, then they would point to some line that most likely does not even contain a reference to the function (it just happens to be called from there, but can be registered somewhere else).

Anyway, I don't really care much if it behaves one way or the other, but I do think that the current behavior is the right one.
 [2013-06-07 14:05 UTC] tyrael@php.net
from the userland developer POV (=debug_backtrace() target audience) the foo call 
happens in the call_user_func line.
generating bogus entry because we unintentionally leak implementation details to 
the userland is a bad thing imo.
I agree that the fixing this via allowing all zend functions to fetch the info 
from the previous frame would be a bad thing, but it wasn't my intention to 
suggest that.
 [2013-06-07 17:08 UTC] laruence@php.net
I understood your point, I just don't know how to fix other related functions 
once.

call_user_func, call_method, reflectionmethod->invoke, reflectionFunction->invoke 
etc.
 [2013-06-07 17:15 UTC] laruence@php.net
hmm, I may find a solution, use tricky ZEND_CALL_VIA_HANDLER fn_flags, for 
call_user_func, may like:

$ git diff
diff --git a/ext/standard/basic_functions.c b/ext/standard/basic_functions.c
index 9c91404..03f836e 100644
--- a/ext/standard/basic_functions.c
+++ b/ext/standard/basic_functions.c
@@ -2983,7 +2983,7 @@ const zend_function_entry basic_functions[] = { /* {{{ */

 	PHP_FE(error_log,
		arginfo_error_log)
 	PHP_FE(error_get_last,
	arginfo_error_get_last)
-	PHP_FE(call_user_func,
	arginfo_call_user_func)
+	ZEND_FENTRY(call_user_func, ZEND_FN(call_user_func), 
arginfo_call_user_func, ZEND_ACC_CALL_VIA_HANDLER|ZEND_ACC_
PUBLIC)
 	PHP_FE(call_user_func_array,
arginfo_call_user_func_array)
 	PHP_DEP_FE(call_user_method,
arginfo_call_user_method)
 	PHP_DEP_FE(call_user_method_array,
arginfo_call_user_method_array)
 [2013-06-09 09:00 UTC] laruence@php.net
The following patch has been added/updated:

Patch Name: add_call_proxy_flag.patch
Revision:   1370768408
URL:        https://bugs.php.net/patch-display.php?bug=64987&patch=add_call_proxy_flag.patch&revision=1370768408
 [2013-06-09 09:02 UTC] laruence@php.net
A patch is added, introduced a new fn_flag ZEND_ACC_CALL_PROXY, applies to 
call_user_func, call_user_func_array, forward_static_call, reflectionMethod-
>invoke etc

and this only flag should only affects debug_backtrace handling ...

thanks
 [2013-06-24 09:17 UTC] dmitry@php.net
In my opinion, the current implementation is the right one.
It follows the simple rule: when we call some function from an internal one we don't track file name and line number (just because we don't have PHP source for it).

I don't think we need to fix this behaviour.

Thanks. Dmitry.
 [2017-10-24 08:03 UTC] kalle@php.net
-Status: Assigned +Status: Open -Assigned To: laruence +Assigned To:
 [2017-10-24 08:30 UTC] requinix@php.net
-Status: Open +Status: Closed -Assigned To: +Assigned To: requinix
 [2017-10-24 08:30 UTC] requinix@php.net
Fixed with PHP 7 since call_user_func() is compiled away. https://3v4l.org/YdubX
 
PHP Copyright © 2001-2024 The PHP Group
All rights reserved.
Last updated: Thu Mar 28 18:01:29 2024 UTC