Skip to content

Implement GH-7922: print_r should not produce a fatal error #8865

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 8 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion Zend/tests/debug_info-error-0.0.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,8 @@ $c = new C(0.0);
var_dump($c);
?>
--EXPECTF--
Fatal error: __debuginfo() must return an array in %s%eZend%etests%edebug_info-error-0.0.php on line %d
Fatal error: Uncaught Error: __debuginfo() must return an array in %s%eZend%etests%edebug_info-error-0.0.php:14
Stack trace:
#0 %s(14): var_dump(Object(C))
#1 {main}
thrown in %s on line 14
6 changes: 5 additions & 1 deletion Zend/tests/debug_info-error-0.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,8 @@ $c = new C(0);
var_dump($c);
?>
--EXPECTF--
Fatal error: __debuginfo() must return an array in %s%eZend%etests%edebug_info-error-0.php on line %d
Fatal error: Uncaught Error: __debuginfo() must return an array in %s:%d
Stack trace:
#0 %s(14): var_dump(Object(C))
#1 {main}
thrown in %s on line 14
6 changes: 5 additions & 1 deletion Zend/tests/debug_info-error-1.0.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,8 @@ $c = new C(1.0);
var_dump($c);
?>
--EXPECTF--
Fatal error: __debuginfo() must return an array in %s%eZend%etests%edebug_info-error-1.0.php on line %d
Fatal error: Uncaught Error: __debuginfo() must return an array in %s%eZend%etests%edebug_info-error-1.0.php:14
Stack trace:
#0 %s(14): var_dump(Object(C))
#1 {main}
thrown in %s on line 14
6 changes: 5 additions & 1 deletion Zend/tests/debug_info-error-1.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,8 @@ $c = new C(1);
var_dump($c);
?>
--EXPECTF--
Fatal error: __debuginfo() must return an array in %s%eZend%etests%edebug_info-error-1.php on line %d
Fatal error: Uncaught Error: __debuginfo() must return an array in %s%eZend%etests%edebug_info-error-1.php:14
Stack trace:
#0 %s(14): var_dump(Object(C))
#1 {main}
thrown in %s on line 14
6 changes: 5 additions & 1 deletion Zend/tests/debug_info-error-empty_str.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,8 @@ $c = new C("");
var_dump($c);
?>
--EXPECTF--
Fatal error: __debuginfo() must return an array in %s%eZend%etests%edebug_info-error-empty_str.php on line %d
Fatal error: Uncaught Error: __debuginfo() must return an array in %s%eZend%etests%edebug_info-error-empty_str.php:14
Stack trace:
#0 %s(14): var_dump(Object(C))
#1 {main}
thrown in %s on line 14
6 changes: 5 additions & 1 deletion Zend/tests/debug_info-error-false.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,8 @@ $c = new C(false);
var_dump($c);
?>
--EXPECTF--
Fatal error: __debuginfo() must return an array in %s%eZend%etests%edebug_info-error-false.php on line %d
Fatal error: Uncaught Error: __debuginfo() must return an array in %s%eZend%etests%edebug_info-error-false.php:14
Stack trace:
#0 %s(14): var_dump(Object(C))
#1 {main}
thrown in %s on line 14
6 changes: 5 additions & 1 deletion Zend/tests/debug_info-error-object.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,8 @@ $c = new C(new stdClass);
var_dump($c);
?>
--EXPECTF--
Fatal error: __debuginfo() must return an array in %s%eZend%etests%edebug_info-error-object.php on line %d
Fatal error: Uncaught Error: __debuginfo() must return an array in %s%eZend%etests%edebug_info-error-object.php:14
Stack trace:
#0 %s(14): var_dump(Object(C))
#1 {main}
thrown in %s on line 14
6 changes: 5 additions & 1 deletion Zend/tests/debug_info-error-resource.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,8 @@ $c = new C(fopen("data:text/plain,Foo", 'r'));
var_dump($c);
?>
--EXPECTF--
Fatal error: __debuginfo() must return an array in %s%eZend%etests%edebug_info-error-resource.php on line %d
Fatal error: Uncaught Error: __debuginfo() must return an array in %s%eZend%etests%edebug_info-error-resource.php:14
Stack trace:
#0 %s(14): var_dump(Object(C))
#1 {main}
thrown in %s on line 14
6 changes: 5 additions & 1 deletion Zend/tests/debug_info-error-str.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,8 @@ $c = new C("foo");
var_dump($c);
?>
--EXPECTF--
Fatal error: __debuginfo() must return an array in %s%eZend%etests%edebug_info-error-str.php on line %d
Fatal error: Uncaught Error: __debuginfo() must return an array in %s%eZend%etests%edebug_info-error-str.php:14
Stack trace:
#0 %s(14): var_dump(Object(C))
#1 {main}
thrown in %s on line 14
6 changes: 5 additions & 1 deletion Zend/tests/debug_info-error-true.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,8 @@ $c = new C(true);
var_dump($c);
?>
--EXPECTF--
Fatal error: __debuginfo() must return an array in %s%eZend%etests%edebug_info-error-true.php on line %d
Fatal error: Uncaught Error: __debuginfo() must return an array in %s%eZend%etests%edebug_info-error-true.php:14
Stack trace:
#0 %s(14): var_dump(Object(C))
#1 {main}
thrown in %s on line 14
105 changes: 105 additions & 0 deletions Zend/tests/gh7922.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
--TEST--
GH-7922 (print_r should not produce a fatal error)
--FILE--
<?php
class A
{
public function __debugInfo()
{
throw new \Error('x');
}
}

echo "Exception in __debugInfo:\n";

try {
print_r(new A());
} catch (\Throwable $e) {
echo $e . "\n";
}

echo "\nException in __debugInfo in anonymous class:\n";

try {
print_r(new class() extends A {
public function __debugInfo(): array
{
throw new \Exception('y');
}
});
} catch (\Throwable $e) {
echo $e . "\n";
}

echo "\nException in __destruct of __debugInfo in anonymous class:\n";

try {
print_r(new class() extends A {
public function __debugInfo(): array
{
$o = new class() {
public function __destruct()
{
throw new \Exception('z_w_assign');
}
};

return ['foo' => 2];
}
});
} catch (\Throwable $e) {
echo $e . "\n";
}

echo "\nException occuring later on (print_r):\n";

try {
print_r(['foo' => 'bar', 'baz' => new A()]);
} catch (\Throwable $e) {
echo $e . "\n";
}

echo "\nException occuring later on (var_dump):\n";

try {
var_dump(['u' => 1], ['foo' => 'bar', 'baz' => new A()], ['u' => 1]);
} catch (\Throwable $e) {
echo $e . "\n";
}

?>
--EXPECTF--
Exception in __debugInfo:
Error: x in %s:%d
Stack trace:
#0 [internal function]: A->__debugInfo()
#1 %s(%d): print_r(Object(A))
#2 {main}

Exception in __debugInfo in anonymous class:
Exception: y in %s:%d
Stack trace:
#0 [internal function]: A@anonymous->__debugInfo()
#1 %s(%d): print_r(Object(A@anonymous))
#2 {main}

Exception in __destruct of __debugInfo in anonymous class:
Exception: z_w_assign in %s:%d
Stack trace:
#0 [internal function]: class@anonymous->__destruct()
#1 %s(%d): print_r(Object(A@anonymous))
#2 {main}

Exception occuring later on (print_r):
Error: x in %s:%d
Stack trace:
#0 [internal function]: A->__debugInfo()
#1 %s(%d): print_r(Array)
#2 {main}

Exception occuring later on (var_dump):
Error: x in %s:%d
Stack trace:
#0 [internal function]: A->__debugInfo()
#1 %s(%d): var_dump(Array)
#2 {main}
40 changes: 24 additions & 16 deletions Zend/zend_object_handlers.c
Original file line number Diff line number Diff line change
Expand Up @@ -164,27 +164,35 @@ ZEND_API HashTable *zend_std_get_debug_info(zend_object *object, int *is_temp) /
}

zend_call_known_instance_method_with_0_params(ce->__debugInfo, object, &retval);
if (Z_TYPE(retval) == IS_ARRAY) {
if (!Z_REFCOUNTED(retval)) {
*is_temp = 1;
return zend_array_dup(Z_ARRVAL(retval));
} else if (Z_REFCOUNT(retval) <= 1) {
if (EG(exception)) {
zval_ptr_dtor(&retval);
return NULL;
}

switch (Z_TYPE(retval)) {
case IS_ARRAY:
if (!Z_REFCOUNTED(retval)) {
*is_temp = 1;
return zend_array_dup(Z_ARRVAL(retval));
} else if (Z_REFCOUNT(retval) <= 1) {
*is_temp = 1;
ht = Z_ARR(retval);
return ht;
} else {
*is_temp = 0;
zval_ptr_dtor(&retval);
return Z_ARRVAL(retval);
}
case IS_NULL:
*is_temp = 1;
ht = Z_ARR(retval);
ht = zend_new_array(0);
return ht;
} else {
*is_temp = 0;
default:
zend_throw_error(NULL, ZEND_DEBUGINFO_FUNC_NAME "() must return an array");
zval_ptr_dtor(&retval);
return Z_ARRVAL(retval);
}
} else if (Z_TYPE(retval) == IS_NULL) {
*is_temp = 1;
ht = zend_new_array(0);
return ht;
return NULL;
}

zend_error_noreturn(E_ERROR, ZEND_DEBUGINFO_FUNC_NAME "() must return an array");

return NULL; /* Compilers are dumb and don't understand that noreturn means that the function does NOT need a return value... */
}
/* }}} */
Expand Down
2 changes: 0 additions & 2 deletions ext/ffi/tests/035.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,4 @@ object(FFI\CData:uint16_t[2])#%d (2) {
[1]=>
int(0)
}
object(FFI\CData:uint16_t[2])#%d (0) {
}
FFI\Exception: Use after free()
3 changes: 3 additions & 0 deletions ext/standard/var.c
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,9 @@ PHPAPI void php_var_dump(zval *struc, int level) /* {{{ */
Z_PROTECT_RECURSION_P(struc);

myht = zend_get_properties_for(struc, ZEND_PROP_PURPOSE_DEBUG);
if (EG(exception)) {
break;
}
class_name = Z_OBJ_HANDLER_P(struc, get_class_name)(Z_OBJ_P(struc));
php_printf("%sobject(%s)#%d (%d) {\n", COMMON, ZSTR_VAL(class_name), Z_OBJ_HANDLE_P(struc), myht ? zend_array_count(myht) : 0);
zend_string_release_ex(class_name, 0);
Expand Down