diff --git a/Zend/tests/ArrayAccess/bug41209.phpt b/Zend/tests/ArrayAccess/bug41209.phpt index 1a7ee8aa515c..f1bc4267d762 100644 --- a/Zend/tests/ArrayAccess/bug41209.phpt +++ b/Zend/tests/ArrayAccess/bug41209.phpt @@ -42,5 +42,6 @@ echo "Done\n"; Fatal error: Uncaught ErrorException: Undefined variable $id in %s:%d Stack trace: #0 %s(%d): env::errorHandler(2, 'Undefined varia...', '%s', %d) -#1 {main} +#1 %s(%d): cache->offsetExists(NULL) +#2 {main} thrown in %s on line %d diff --git a/Zend/tests/bitwise_not_precision_exception.phpt b/Zend/tests/bitwise_not_precision_exception.phpt index e28bf8f4e17b..87631788d561 100644 --- a/Zend/tests/bitwise_not_precision_exception.phpt +++ b/Zend/tests/bitwise_not_precision_exception.phpt @@ -12,4 +12,5 @@ try { } ?> --EXPECT-- +int(-1) The float INF is not representable as an int, cast occurred diff --git a/Zend/tests/bug63206.phpt b/Zend/tests/bug63206.phpt index 6aba55eca1ba..9cb056d48b5c 100644 --- a/Zend/tests/bug63206.phpt +++ b/Zend/tests/bug63206.phpt @@ -22,8 +22,10 @@ set_error_handler(function() { $triggerNotice1++; $triggerNotice2++; ?> ---EXPECT-- +--EXPECTF-- Second handler -Internal handler + +Warning: Undefined variable $triggerInternalNotice in %s on line %d Second handler -Internal handler + +Warning: Undefined variable $triggerInternalNotice in %s on line %d diff --git a/Zend/tests/bug70662.phpt b/Zend/tests/bug70662.phpt index ab540c9d16e5..2bda8141bab4 100644 --- a/Zend/tests/bug70662.phpt +++ b/Zend/tests/bug70662.phpt @@ -14,5 +14,5 @@ var_dump($a); --EXPECT-- array(1) { ["b"]=> - int(2) + int(1) } diff --git a/Zend/tests/bug70785.phpt b/Zend/tests/bug70785.phpt index 05b1b6afa0a0..16789a436ab0 100644 --- a/Zend/tests/bug70785.phpt +++ b/Zend/tests/bug70785.phpt @@ -7,19 +7,32 @@ set_error_handler(function($no, $msg) { throw new Exception($msg); }); -try { +function smart_branch() { if ($a === null) { // ZEND_VM_SMART_BRANCH - undefined_function('Null'); + return 'branch'; } -} catch (Exception $e) { + return 'no branch'; } -try { +function next_opcode() { $c === 3; // ZEND_VM_NEXT_OPCODE - undefined_function(); + return 'done'; +} + +try { + smart_branch(); } catch (Exception $e) { + echo $e->getMessage(), PHP_EOL; } + +try { + next_opcode(); +} catch (Exception $e) { + echo $e->getMessage(), PHP_EOL; +} +echo "okey\n"; ?> -okey --EXPECT-- +Undefined variable $a +Undefined variable $c okey diff --git a/Zend/tests/bug72101.phpt b/Zend/tests/bug72101.phpt index 6362a7ccb303..64393f07ba98 100644 --- a/Zend/tests/bug72101.phpt +++ b/Zend/tests/bug72101.phpt @@ -79,9 +79,10 @@ $foo->bar($a, $b, $c); Fatal error: Uncaught Error: Class "DoesNotExists" not found in %s:%d Stack trace: #0 %s(%d): {closure:%s:%d}(2, 'MethodCallbackB...', '%s', 8) -#1 %sbug72101.php(%d): PHPUnit_Framework_MockObject_Stub_ReturnCallback->invoke(Object(PHPUnit_Framework_MockObject_Invocation_Static)) -#2 %sbug72101.php(%d): PHPUnit_Framework_MockObject_Matcher->invoked(Object(PHPUnit_Framework_MockObject_Invocation_Static)) -#3 %sbug72101.php(%d): PHPUnit_Framework_MockObject_InvocationMocker->invoke(Object(PHPUnit_Framework_MockObject_Invocation_Static)) -#4 %sbug72101.php(%d): Mock_MethodCallbackByReference_7b180d26->bar(0, 0, 0) -#5 {main} +#1 %sbug72101.php(%d): MethodCallbackByReference->callback(0, 0, 0) +#2 %sbug72101.php(%d): PHPUnit_Framework_MockObject_Stub_ReturnCallback->invoke(Object(PHPUnit_Framework_MockObject_Invocation_Static)) +#3 %sbug72101.php(%d): PHPUnit_Framework_MockObject_Matcher->invoked(Object(PHPUnit_Framework_MockObject_Invocation_Static)) +#4 %sbug72101.php(%d): PHPUnit_Framework_MockObject_InvocationMocker->invoke(Object(PHPUnit_Framework_MockObject_Invocation_Static)) +#5 %sbug72101.php(%d): Mock_MethodCallbackByReference_7b180d26->bar(0, 0, 0) +#6 {main} thrown in %sbug72101.php on line %d diff --git a/Zend/tests/bug76534.phpt b/Zend/tests/bug76534.phpt index c9c897110d6c..9cec1962773b 100644 --- a/Zend/tests/bug76534.phpt +++ b/Zend/tests/bug76534.phpt @@ -10,8 +10,12 @@ $x = "foo"; $y = &$x["2bar"]; ?> --EXPECTF-- -Fatal error: Uncaught Exception: Illegal string offset "2bar" in %s:%d +Fatal error: Uncaught Error: Cannot create references to/from string offsets in %s:%d +Stack trace: +#0 {main} + +Next Exception: Illegal string offset "2bar" in %s:%d Stack trace: #0 %s(%d): {closure:%s:%d}(2, 'Illegal string ...', '%s', 7) #1 {main} - thrown in %sbug76534.php on line %d + thrown in %s on line %d \ No newline at end of file diff --git a/Zend/tests/bug78598.phpt b/Zend/tests/bug78598.phpt index 7e3559f27c26..976040204865 100644 --- a/Zend/tests/bug78598.phpt +++ b/Zend/tests/bug78598.phpt @@ -25,7 +25,31 @@ var_dump($my_var); ?> --EXPECT-- -int(0) -int(0) -int(0) -int(0) +array(1) { + [0]=> + string(3) "xyz" +} +array(1) { + [0]=> + array(1) { + [0]=> + array(1) { + [0]=> + string(3) "xyz" + } + } +} +array(1) { + ["foo"]=> + string(3) "xyz" +} +array(1) { + ["foo"]=> + array(1) { + ["bar"]=> + array(1) { + ["baz"]=> + string(3) "xyz" + } + } +} diff --git a/Zend/tests/bug79784.phpt b/Zend/tests/bug79784.phpt index be1cd729e969..83e64626bba7 100644 --- a/Zend/tests/bug79784.phpt +++ b/Zend/tests/bug79784.phpt @@ -15,6 +15,18 @@ var_dump($a); ?> --EXPECT-- -NULL -NULL -NULL +array(1) { + [""]=> + string(1) "x" +} +array(1) { + [""]=> + string(1) "x" +} +array(1) { + [""]=> + array(1) { + [""]=> + string(1) "x" + } +} diff --git a/Zend/tests/bug79793.phpt b/Zend/tests/bug79793.phpt index 5491a2669ed9..4af55ffd07cb 100644 --- a/Zend/tests/bug79793.phpt +++ b/Zend/tests/bug79793.phpt @@ -18,15 +18,15 @@ var_dump($ary); ?> --EXPECT-- -Undefined array key "foobar" array(1) { ["foobar"]=> int(1) } -Undefined array key "foobarbaz" +Undefined array key "foobar" array(2) { ["foobar"]=> int(1) ["foobarbaz"]=> int(1) } +Undefined array key "foobarbaz" diff --git a/Zend/tests/closures/closure_031.phpt b/Zend/tests/closures/closure_031.phpt index 19f3dc6e3212..7f9d3780e4ff 100644 --- a/Zend/tests/closures/closure_031.phpt +++ b/Zend/tests/closures/closure_031.phpt @@ -15,5 +15,5 @@ try { } ?> --EXPECT-- -Warning: Undefined property: Closure::$a NULL +Warning: Undefined property: Closure::$a diff --git a/Zend/tests/compound_assign_failure.phpt b/Zend/tests/compound_assign_failure.phpt index 0dc9af85f253..4ff85da4561c 100644 --- a/Zend/tests/compound_assign_failure.phpt +++ b/Zend/tests/compound_assign_failure.phpt @@ -22,14 +22,16 @@ try { set_error_handler(function($type, $msg) { throw new Exception($msg); }); +function concat(&$a, $b) { $a .= $b; } + try { $a = []; - $a .= "foo"; + concat($a, "foo"); } catch (Throwable $e) { var_dump($a); } try { $a = "foo"; - $a .= []; + concat($a, []); } catch (Throwable $e) { var_dump($a); } $x = new stdClass; @@ -202,9 +204,8 @@ var_dump($x); int(1) int(1) int(1) -array(0) { -} -string(3) "foo" +string(8) "Arrayfoo" +string(8) "fooArray" object(stdClass)#%d (0) { } int(1) diff --git a/Zend/tests/concat/bug81705.phpt b/Zend/tests/concat/bug81705.phpt index 1c00b1c77d4b..10eea6c0a35c 100644 --- a/Zend/tests/concat/bug81705.phpt +++ b/Zend/tests/concat/bug81705.phpt @@ -15,5 +15,5 @@ $my_var .= $GLOBALS["arr"]; var_dump($my_var); ?> --EXPECT-- -error -string(6) "aArray" \ No newline at end of file +string(6) "aArray" +error \ No newline at end of file diff --git a/Zend/tests/dynamic_prop_deprecation_002.phpt b/Zend/tests/dynamic_prop_deprecation_002.phpt index bd0d3aa5a7df..20182d2a8923 100644 --- a/Zend/tests/dynamic_prop_deprecation_002.phpt +++ b/Zend/tests/dynamic_prop_deprecation_002.phpt @@ -15,4 +15,3 @@ try { ?> --EXPECT-- Err: Creation of dynamic property class@anonymous::$y is deprecated -Exception: Cannot create dynamic property class@anonymous::$y diff --git a/Zend/tests/exception_in_nested_rope.phpt b/Zend/tests/exception_in_nested_rope.phpt index 7afd1412354a..27cfe6d77168 100644 --- a/Zend/tests/exception_in_nested_rope.phpt +++ b/Zend/tests/exception_in_nested_rope.phpt @@ -15,4 +15,9 @@ try { ?> --EXPECTF-- Deprecated: Using ${expr} (variable variables) in strings is deprecated, use {${expr}} instead in %s on line %d -Exception + +Fatal error: Uncaught Exception in %s:%d +Stack trace: +#0 %s(%d): {closure:%s:%d}(2, 'Undefined varia...', '%s', %d) +#1 {main} + thrown in %s on line %d diff --git a/Zend/tests/exception_in_rope_end.phpt b/Zend/tests/exception_in_rope_end.phpt index 3e6402c60f42..da6fa6a61903 100644 --- a/Zend/tests/exception_in_rope_end.phpt +++ b/Zend/tests/exception_in_rope_end.phpt @@ -13,5 +13,9 @@ try { } ?> ---EXPECT-- -Exception +--EXPECTF-- +Fatal error: Uncaught Exception in %s:%d +Stack trace: +#0 %s(%d): {closure:%s:%d}(2, 'Undefined varia...', '%s', %d) +#1 {main} + thrown in %s on line %d diff --git a/Zend/tests/exceptions/exception_before_fatal.phpt b/Zend/tests/exceptions/exception_before_fatal.phpt index edc63b2394cd..847cc2cd8f96 100644 --- a/Zend/tests/exceptions/exception_before_fatal.phpt +++ b/Zend/tests/exceptions/exception_before_fatal.phpt @@ -61,4 +61,4 @@ string(23) "Undefined variable $foo" string(23) "Undefined variable $foo" string(23) "Undefined variable $foo" string(23) "Undefined variable $foo" -string(23) "Undefined variable $foo" +string(23) "Undefined variable $foo" \ No newline at end of file diff --git a/Zend/tests/falsetoarray_002.phpt b/Zend/tests/falsetoarray_002.phpt index c01b79954596..639cd51b3661 100644 --- a/Zend/tests/falsetoarray_002.phpt +++ b/Zend/tests/falsetoarray_002.phpt @@ -11,5 +11,13 @@ $a[0][$d]='b'; var_dump($a); ?> --EXPECT-- +array(1) { + [0]=> + array(1) { + [""]=> + string(1) "b" + } +} Err: Automatic conversion of false to array is deprecated -string(0) "" +Err: Undefined variable $d +Err: Using null as an array offset is deprecated, use an empty string instead diff --git a/Zend/tests/falsetoarray_003.phpt b/Zend/tests/falsetoarray_003.phpt index 117e443ef958..d62dff41941c 100644 --- a/Zend/tests/falsetoarray_003.phpt +++ b/Zend/tests/falsetoarray_003.phpt @@ -11,6 +11,6 @@ $a=[]; ?> DONE --EXPECTF-- +DONE Err: The float %f is not representable as an int, cast occurred Err: Undefined array key %i -DONE diff --git a/Zend/tests/first_class_callable/constexpr/error_static_call_trait_method_002.phpt b/Zend/tests/first_class_callable/constexpr/error_static_call_trait_method_002.phpt index 97831a8d65f0..51c22102bddd 100644 --- a/Zend/tests/first_class_callable/constexpr/error_static_call_trait_method_002.phpt +++ b/Zend/tests/first_class_callable/constexpr/error_static_call_trait_method_002.phpt @@ -28,4 +28,13 @@ try { ?> --EXPECT-- +object(Closure)#2 (2) { + ["function"]=> + string(13) "Foo::myMethod" + ["parameter"]=> + array(1) { + ["$foo"]=> + string(10) "" + } +} Caught: Calling static trait method Foo::myMethod is deprecated, it should only be called on a class using the trait diff --git a/Zend/tests/get_class_basic.phpt b/Zend/tests/get_class_basic.phpt index f6dcbccb6916..d8809ba9d9cf 100644 --- a/Zend/tests/get_class_basic.phpt +++ b/Zend/tests/get_class_basic.phpt @@ -54,6 +54,7 @@ $f1->testNull(); echo "Done\n"; ?> --EXPECTF-- +string(3) "foo" Calling get_class() without arguments is deprecated Deprecated: Calling get_class() without arguments is deprecated in %s on line %d diff --git a/Zend/tests/gh16792.phpt b/Zend/tests/gh16792.phpt new file mode 100644 index 000000000000..ed2eae538d3d --- /dev/null +++ b/Zend/tests/gh16792.phpt @@ -0,0 +1,16 @@ +--TEST-- +GH-16792 (Assertion failure when an error handler mutates $GLOBALS during a by-ref sort) +--FILE-- + +--EXPECT-- +ok diff --git a/Zend/tests/gh17416.phpt b/Zend/tests/gh17416.phpt new file mode 100644 index 000000000000..1fd0e7d5ce04 --- /dev/null +++ b/Zend/tests/gh17416.phpt @@ -0,0 +1,30 @@ +--TEST-- +GH-17416 (Assertion failure when a throwing error handler runs while an array is unset during foreach) +--FILE-- + $val) { + if ($val === 2) { + unset($a[$fusion]); + } + } +} +function foo($a, bool $b): bool { + do { + $res = bar($a); + } while ($res === null && $n !== $n2); +} +foo([2, 'a' => 5], false); +?> +--EXPECTF-- +Fatal error: Uncaught Exception: Undefined variable $fusion in %s:%d +Stack trace: +#0 %s(%d): Error2Exception(2, 'Undefined varia...', '%s', %d) +#1 %s(%d): bar(Array) +#2 %s(%d): foo(Array, false) +#3 {main} + thrown in %s on line %d diff --git a/Zend/tests/gh21245.phpt b/Zend/tests/gh21245.phpt new file mode 100644 index 000000000000..c922b3865c3c --- /dev/null +++ b/Zend/tests/gh21245.phpt @@ -0,0 +1,21 @@ +--TEST-- +GH-21245 (Use-after-free in ASSIGN_DIM when the error handler grows the array holding a typed-property reference) +--FILE-- +x = ""; +$a[0] =& $test->x; +var_dump($a[0] = $v); +?> +--EXPECTF-- +Fatal error: Uncaught TypeError: Cannot assign null to reference held by property Test::$x of type string in %s:%d +Stack trace: +#0 {main} + thrown in %s on line %d diff --git a/Zend/tests/gh22419.phpt b/Zend/tests/gh22419.phpt new file mode 100644 index 000000000000..f6e3241801d3 --- /dev/null +++ b/Zend/tests/gh22419.phpt @@ -0,0 +1,19 @@ +--TEST-- +GH-22419 (UAF/refcount assertion in array-offset concat when the error handler replaces the LHS) +--FILE-- + +--EXPECT-- +ok diff --git a/Zend/tests/in-de-crement/incdec_bool_exception.phpt b/Zend/tests/in-de-crement/incdec_bool_exception.phpt index f819af1e2395..d7ccf55e406f 100644 --- a/Zend/tests/in-de-crement/incdec_bool_exception.phpt +++ b/Zend/tests/in-de-crement/incdec_bool_exception.phpt @@ -7,15 +7,22 @@ set_error_handler(function($severity, $m) { throw new Exception($m, $severity); }); +function inc(&$v) { + $v++; +} +function dec(&$v) { + $v--; +} + $values = [false, true]; foreach ($values as $value) { try { - $value++; + inc($value); } catch (\Exception $e) { echo $e->getMessage(), PHP_EOL; } try { - $value--; + dec($value); } catch (\Exception $e) { echo $e->getMessage(), PHP_EOL; } diff --git a/Zend/tests/in-de-crement/incdec_strings_exception.phpt b/Zend/tests/in-de-crement/incdec_strings_exception.phpt index 5acbb1041222..b5b00e6c00d6 100644 --- a/Zend/tests/in-de-crement/incdec_strings_exception.phpt +++ b/Zend/tests/in-de-crement/incdec_strings_exception.phpt @@ -13,6 +13,13 @@ set_error_handler(function($severity, $m) { throw new Exception($m, $severity); }); +function inc(&$v) { + $v++; +} +function dec(&$v) { + $v--; +} + $values = [ '', ' ', @@ -26,47 +33,49 @@ $values = [ '🐘' ]; foreach ($values as $value) { + $v = $value; try { - $value++; + inc($v); } catch (\Exception $e) { echo $e->getMessage(), PHP_EOL; } - var_dump($value); + var_dump($v); + $v = $value; try { - $value--; + dec($v); } catch (\Exception $e) { echo $e->getMessage(), PHP_EOL; } - var_dump($value); + var_dump($v); } ?> --EXPECT-- Deprecated: Increment on non-numeric string is deprecated, use str_increment() instead -string(0) "" +string(1) "1" Deprecated: Decrement on empty string is deprecated as non-numeric -string(0) "" +int(-1) Deprecated: Increment on non-numeric string is deprecated, use str_increment() instead string(1) " " Deprecated: Decrement on non-numeric string has no effect and is deprecated string(1) " " Deprecated: Increment on non-numeric string is deprecated, use str_increment() instead -string(4) "199A" +string(4) "199B" Deprecated: Decrement on non-numeric string has no effect and is deprecated string(4) "199A" Deprecated: Increment on non-numeric string is deprecated, use str_increment() instead -string(4) "A199" +string(4) "A200" Deprecated: Decrement on non-numeric string has no effect and is deprecated string(4) "A199" Deprecated: Increment on non-numeric string is deprecated, use str_increment() instead -string(4) "199Z" +string(4) "200A" Deprecated: Decrement on non-numeric string has no effect and is deprecated string(4) "199Z" Deprecated: Increment on non-numeric string is deprecated, use str_increment() instead -string(4) "Z199" +string(4) "Z200" Deprecated: Decrement on non-numeric string has no effect and is deprecated string(4) "Z199" Deprecated: Increment on non-numeric string is deprecated, use str_increment() instead -string(11) "Hello world" +string(11) "Hello worle" Deprecated: Decrement on non-numeric string has no effect and is deprecated string(11) "Hello world" Deprecated: Increment on non-numeric string is deprecated, use str_increment() instead diff --git a/Zend/tests/in-de-crement/incdec_undef.phpt b/Zend/tests/in-de-crement/incdec_undef.phpt index db560e31c99f..549c64fea87e 100644 --- a/Zend/tests/in-de-crement/incdec_undef.phpt +++ b/Zend/tests/in-de-crement/incdec_undef.phpt @@ -14,6 +14,7 @@ unset($x); var_dump(++$x); ?> --EXPECT-- +NULL Undefined variable $x Decrement on type null has no effect, this will change in the next major version of PHP NULL @@ -21,6 +22,5 @@ Undefined variable $x NULL Undefined variable $x Decrement on type null has no effect, this will change in the next major version of PHP -NULL -Undefined variable $x int(1) +Undefined variable $x diff --git a/Zend/tests/in-de-crement/increment_diagnostic_change_type.phpt b/Zend/tests/in-de-crement/increment_diagnostic_change_type.phpt index 11a0edac1917..b55c8c6b811e 100644 --- a/Zend/tests/in-de-crement/increment_diagnostic_change_type.phpt +++ b/Zend/tests/in-de-crement/increment_diagnostic_change_type.phpt @@ -31,8 +31,8 @@ var_dump($x); DONE --EXPECT-- string(1) "1" -string(74) "Increment on non-numeric string is deprecated, use str_increment() instead" string(4) "foo!" string(74) "Increment on non-numeric string is deprecated, use str_increment() instead" string(1) "!" +string(74) "Increment on non-numeric string is deprecated, use str_increment() instead" DONE diff --git a/Zend/tests/in-de-crement/increment_diagnostic_change_type_do_operator.phpt b/Zend/tests/in-de-crement/increment_diagnostic_change_type_do_operator.phpt index 8e63ad6aeed4..9308a1a7bd3d 100644 --- a/Zend/tests/in-de-crement/increment_diagnostic_change_type_do_operator.phpt +++ b/Zend/tests/in-de-crement/increment_diagnostic_change_type_do_operator.phpt @@ -23,8 +23,8 @@ var_dump($x); ?> DONE --EXPECT-- -string(74) "Increment on non-numeric string is deprecated, use str_increment() instead" string(4) "foo!" string(74) "Increment on non-numeric string is deprecated, use str_increment() instead" string(1) "!" +string(74) "Increment on non-numeric string is deprecated, use str_increment() instead" DONE diff --git a/Zend/tests/in-de-crement/oss-fuzz-60709_globals_unset_after_undef_warning.phpt b/Zend/tests/in-de-crement/oss-fuzz-60709_globals_unset_after_undef_warning.phpt index e981800eeac1..1f1fc7228134 100644 --- a/Zend/tests/in-de-crement/oss-fuzz-60709_globals_unset_after_undef_warning.phpt +++ b/Zend/tests/in-de-crement/oss-fuzz-60709_globals_unset_after_undef_warning.phpt @@ -21,16 +21,16 @@ var_dump(++$x); ?> --EXPECT-- POST DEC +NULL Undefined variable $x Decrement on type null has no effect, this will change in the next major version of PHP -NULL POST INC -Undefined variable $x NULL +Undefined variable $x PRE DEC +NULL Undefined variable $x Decrement on type null has no effect, this will change in the next major version of PHP -NULL PRE INC -Undefined variable $x int(1) +Undefined variable $x diff --git a/Zend/tests/in-de-crement/oss-fuzz-62294_globals_unset_after_string_warning.phpt b/Zend/tests/in-de-crement/oss-fuzz-62294_globals_unset_after_string_warning.phpt index f52dff52e631..e39184e68154 100644 --- a/Zend/tests/in-de-crement/oss-fuzz-62294_globals_unset_after_string_warning.phpt +++ b/Zend/tests/in-de-crement/oss-fuzz-62294_globals_unset_after_string_warning.phpt @@ -25,14 +25,14 @@ var_dump(++$x); ?> --EXPECT-- POST DEC -Decrement on non-numeric string has no effect and is deprecated string(1) " " -PRE DEC Decrement on non-numeric string has no effect and is deprecated +PRE DEC string(1) " " +Decrement on non-numeric string has no effect and is deprecated POST INC -Increment on non-numeric string is deprecated, use str_increment() instead string(1) " " -PRE INC Increment on non-numeric string is deprecated, use str_increment() instead +PRE INC string(1) " " +Increment on non-numeric string is deprecated, use str_increment() instead diff --git a/Zend/tests/in-de-crement/unset_globals_in_error_handler.phpt b/Zend/tests/in-de-crement/unset_globals_in_error_handler.phpt index 31ffea22467b..88302517ce05 100644 --- a/Zend/tests/in-de-crement/unset_globals_in_error_handler.phpt +++ b/Zend/tests/in-de-crement/unset_globals_in_error_handler.phpt @@ -78,53 +78,53 @@ unset($x); --EXPECT-- NULL (only --) POST DEC -Decrement on type null has no effect, this will change in the next major version of PHP NULL -PRE DEC Decrement on type null has no effect, this will change in the next major version of PHP +PRE DEC NULL +Decrement on type null has no effect, this will change in the next major version of PHP Empty string POST INC -Increment on non-numeric string is deprecated, use str_increment() instead string(0) "" +Increment on non-numeric string is deprecated, use str_increment() instead POST DEC -Decrement on empty string is deprecated as non-numeric string(0) "" +Decrement on empty string is deprecated as non-numeric PRE INC -Increment on non-numeric string is deprecated, use str_increment() instead string(1) "1" +Increment on non-numeric string is deprecated, use str_increment() instead PRE DEC -Decrement on empty string is deprecated as non-numeric int(-1) +Decrement on empty string is deprecated as non-numeric Non fill ASCII (only ++) POST INC -Increment on non-numeric string is deprecated, use str_increment() instead string(4) " ad " -PRE INC Increment on non-numeric string is deprecated, use str_increment() instead +PRE INC string(4) " ad " +Increment on non-numeric string is deprecated, use str_increment() instead Bool POST INC -Increment on type bool has no effect, this will change in the next major version of PHP bool(false) +Increment on type bool has no effect, this will change in the next major version of PHP POST DEC -Decrement on type bool has no effect, this will change in the next major version of PHP bool(false) +Decrement on type bool has no effect, this will change in the next major version of PHP PRE INC -Increment on type bool has no effect, this will change in the next major version of PHP bool(false) +Increment on type bool has no effect, this will change in the next major version of PHP PRE DEC -Decrement on type bool has no effect, this will change in the next major version of PHP bool(false) +Decrement on type bool has no effect, this will change in the next major version of PHP POST INC -Increment on type bool has no effect, this will change in the next major version of PHP bool(true) +Increment on type bool has no effect, this will change in the next major version of PHP POST DEC -Decrement on type bool has no effect, this will change in the next major version of PHP bool(true) +Decrement on type bool has no effect, this will change in the next major version of PHP PRE INC -Increment on type bool has no effect, this will change in the next major version of PHP bool(true) +Increment on type bool has no effect, this will change in the next major version of PHP PRE DEC -Decrement on type bool has no effect, this will change in the next major version of PHP bool(true) +Decrement on type bool has no effect, this will change in the next major version of PHP diff --git a/Zend/tests/in-de-crement/unset_object_property_in_error_handler.phpt b/Zend/tests/in-de-crement/unset_object_property_in_error_handler.phpt index 4d2a4705588a..b00ba707cecf 100644 --- a/Zend/tests/in-de-crement/unset_object_property_in_error_handler.phpt +++ b/Zend/tests/in-de-crement/unset_object_property_in_error_handler.phpt @@ -87,57 +87,57 @@ var_dump(--$c->a); unset($c->a); ?> --EXPECT-- -string(87) "Decrement on type null has no effect, this will change in the next major version of PHP" NULL +string(87) "Decrement on type null has no effect, this will change in the next major version of PHP" NULL (only --) POST DEC -string(87) "Decrement on type null has no effect, this will change in the next major version of PHP" NULL -PRE DEC string(87) "Decrement on type null has no effect, this will change in the next major version of PHP" +PRE DEC NULL +string(87) "Decrement on type null has no effect, this will change in the next major version of PHP" Empty string POST INC -string(74) "Increment on non-numeric string is deprecated, use str_increment() instead" string(0) "" +string(74) "Increment on non-numeric string is deprecated, use str_increment() instead" POST DEC -string(54) "Decrement on empty string is deprecated as non-numeric" string(0) "" +string(54) "Decrement on empty string is deprecated as non-numeric" PRE INC -string(74) "Increment on non-numeric string is deprecated, use str_increment() instead" string(1) "1" +string(74) "Increment on non-numeric string is deprecated, use str_increment() instead" PRE DEC -string(54) "Decrement on empty string is deprecated as non-numeric" int(-1) +string(54) "Decrement on empty string is deprecated as non-numeric" Non fill ASCII (only ++) POST INC -string(74) "Increment on non-numeric string is deprecated, use str_increment() instead" string(4) " ad " -PRE INC string(74) "Increment on non-numeric string is deprecated, use str_increment() instead" +PRE INC string(4) " ad " +string(74) "Increment on non-numeric string is deprecated, use str_increment() instead" Bool POST INC -string(87) "Increment on type bool has no effect, this will change in the next major version of PHP" bool(false) +string(87) "Increment on type bool has no effect, this will change in the next major version of PHP" POST DEC -string(87) "Decrement on type bool has no effect, this will change in the next major version of PHP" bool(false) +string(87) "Decrement on type bool has no effect, this will change in the next major version of PHP" PRE INC -string(87) "Increment on type bool has no effect, this will change in the next major version of PHP" bool(false) +string(87) "Increment on type bool has no effect, this will change in the next major version of PHP" PRE DEC -string(87) "Decrement on type bool has no effect, this will change in the next major version of PHP" bool(false) +string(87) "Decrement on type bool has no effect, this will change in the next major version of PHP" POST INC -string(87) "Increment on type bool has no effect, this will change in the next major version of PHP" bool(true) +string(87) "Increment on type bool has no effect, this will change in the next major version of PHP" POST DEC -string(87) "Decrement on type bool has no effect, this will change in the next major version of PHP" bool(true) +string(87) "Decrement on type bool has no effect, this will change in the next major version of PHP" PRE INC -string(87) "Increment on type bool has no effect, this will change in the next major version of PHP" bool(true) +string(87) "Increment on type bool has no effect, this will change in the next major version of PHP" PRE DEC -string(87) "Decrement on type bool has no effect, this will change in the next major version of PHP" bool(true) +string(87) "Decrement on type bool has no effect, this will change in the next major version of PHP" diff --git a/Zend/tests/in-de-crement/unset_property_converted_to_obj_in_error_handler.phpt b/Zend/tests/in-de-crement/unset_property_converted_to_obj_in_error_handler.phpt index d435c9fb4029..55d3283ead91 100644 --- a/Zend/tests/in-de-crement/unset_property_converted_to_obj_in_error_handler.phpt +++ b/Zend/tests/in-de-crement/unset_property_converted_to_obj_in_error_handler.phpt @@ -20,6 +20,5 @@ try { var_dump($c->a); ?> --EXPECT-- -Cannot increment stdClass object(stdClass)#2 (0) { } diff --git a/Zend/tests/inheritance/deprecation_to_exception_during_inheritance_can_be_caught.phpt b/Zend/tests/inheritance/deprecation_to_exception_during_inheritance_can_be_caught.phpt index 7a59cca70bd6..77020277f3b8 100644 --- a/Zend/tests/inheritance/deprecation_to_exception_during_inheritance_can_be_caught.phpt +++ b/Zend/tests/inheritance/deprecation_to_exception_during_inheritance_can_be_caught.phpt @@ -24,7 +24,7 @@ var_dump(new C()); ?> --EXPECTF-- -Exception: Return type of C::getTimezone() should either be compatible with DateTime::getTimezone(): DateTimeZone|false, or the #[\ReturnTypeWillChange] attribute should be used to temporarily suppress the notice +Exception: Return type of C::getTimestamp() should either be compatible with DateTime::getTimestamp(): int, or the #[\ReturnTypeWillChange] attribute should be used to temporarily suppress the notice object(C)#%d (3) { ["date"]=> string(%d) "%s" @@ -32,4 +32,4 @@ object(C)#%d (3) { int(3) ["timezone"]=> string(3) "UTC" -} +} \ No newline at end of file diff --git a/Zend/tests/offsets/array_offset_002.phpt b/Zend/tests/offsets/array_offset_002.phpt index 4d56258ef8b8..ece8a53c7215 100644 --- a/Zend/tests/offsets/array_offset_002.phpt +++ b/Zend/tests/offsets/array_offset_002.phpt @@ -12,7 +12,9 @@ function x(&$s){ x($y); var_dump($y); ?> ---EXPECT-- +--EXPECTF-- Err: The float 1.0E+20 is not representable as an int, cast occurred -array(0) { +array(1) { + [%d]=> + int(1) } diff --git a/Zend/tests/offsets/null_offset_dep_promoted.phpt b/Zend/tests/offsets/null_offset_dep_promoted.phpt index c26095d4f745..55145c88837a 100644 --- a/Zend/tests/offsets/null_offset_dep_promoted.phpt +++ b/Zend/tests/offsets/null_offset_dep_promoted.phpt @@ -13,5 +13,9 @@ try { echo $e::class, ': ', $e->getMessage(), PHP_EOL; } ?> ---EXPECT-- -Exception: Using null as an array offset is deprecated, use an empty string instead +--EXPECTF-- +Fatal error: Uncaught Exception: Using null as an array offset is deprecated, use an empty string instead in %s:%d +Stack trace: +#0 %s(%d): {closure:%s:%d}(%d, 'Using null as a...', '%s', %d) +#1 {main} + thrown in %s on line %d diff --git a/Zend/tests/offsets/null_offset_no_uaf.phpt b/Zend/tests/offsets/null_offset_no_uaf.phpt index 38a1b9868345..3352438688fc 100644 --- a/Zend/tests/offsets/null_offset_no_uaf.phpt +++ b/Zend/tests/offsets/null_offset_no_uaf.phpt @@ -13,5 +13,5 @@ $ary[null] = 1; echo "\nSuccess\n"; ?> --EXPECTF-- -Using null as an array offset is deprecated, use an empty string instead Success +Using null as an array offset is deprecated, use an empty string instead diff --git a/Zend/tests/offsets/null_offset_unset_via_error_handler.phpt b/Zend/tests/offsets/null_offset_unset_via_error_handler.phpt index cfaa2b3b20d9..1dd363bab30b 100644 --- a/Zend/tests/offsets/null_offset_unset_via_error_handler.phpt +++ b/Zend/tests/offsets/null_offset_unset_via_error_handler.phpt @@ -14,6 +14,5 @@ $b = [0, null => $a]; echo "\nSuccess\n"; ?> --EXPECTF-- -string(72) "Using null as an array offset is deprecated, use an empty string instead" - Success +string(72) "Using null as an array offset is deprecated, use an empty string instead" diff --git a/Zend/tests/operator_unsupported_types.phpt b/Zend/tests/operator_unsupported_types.phpt index 904ac402b372..1f85bfee871e 100644 --- a/Zend/tests/operator_unsupported_types.phpt +++ b/Zend/tests/operator_unsupported_types.phpt @@ -2104,7 +2104,7 @@ Cannot increment stdClass Cannot decrement stdClass Cannot increment resource Cannot decrement resource -Warning: Increment on non-numeric string is deprecated, use str_increment() instead No error for fop++ -Warning: Decrement on non-numeric string has no effect and is deprecated +Warning: Increment on non-numeric string is deprecated, use str_increment() instead No error for foo-- +Warning: Decrement on non-numeric string has no effect and is deprecated \ No newline at end of file diff --git a/Zend/tests/oss_fuzz_54325.phpt b/Zend/tests/oss_fuzz_54325.phpt index d998acf1ffed..18b108681c2d 100644 --- a/Zend/tests/oss_fuzz_54325.phpt +++ b/Zend/tests/oss_fuzz_54325.phpt @@ -14,6 +14,5 @@ $$x++; var_dump($x); ?> --EXPECT-- +string(3) "oof" string(23) "Undefined variable $oof" -object(stdClass)#2 (0) { -} diff --git a/Zend/tests/oss_fuzz_61712.phpt b/Zend/tests/oss_fuzz_61712.phpt index 5e3aa9060fde..655fb56c1421 100644 --- a/Zend/tests/oss_fuzz_61712.phpt +++ b/Zend/tests/oss_fuzz_61712.phpt @@ -16,5 +16,5 @@ $c->a %= 10; var_dump($c->a); ?> --EXPECT-- -Undefined property: C::$a int(0) +Undefined property: C::$a diff --git a/Zend/tests/oss_fuzz_61712b.phpt b/Zend/tests/oss_fuzz_61712b.phpt index 8ee93b97b432..bfb3d2757366 100644 --- a/Zend/tests/oss_fuzz_61712b.phpt +++ b/Zend/tests/oss_fuzz_61712b.phpt @@ -16,5 +16,5 @@ $c->a %= 10; var_dump($c->a); ?> --EXPECT-- +int(0) Undefined property: C::$a -int(5) diff --git a/Zend/tests/str_offset_006.phpt b/Zend/tests/str_offset_006.phpt index 3cdf91656bd2..01325defe954 100644 --- a/Zend/tests/str_offset_006.phpt +++ b/Zend/tests/str_offset_006.phpt @@ -9,8 +9,13 @@ set_error_handler(function($code, $msg) { $a[$y]=$a.=($y); var_dump($a); ?> ---EXPECT-- +--EXPECTF-- Err: Undefined variable $y +Err: Undefined variable $a Err: Undefined variable $y Err: String offset cast occurred -NULL + +Fatal error: Uncaught Error: Cannot assign an empty string to a string offset in %s:%d +Stack trace: +#0 {main} + thrown in %s on line %d \ No newline at end of file diff --git a/Zend/tests/str_offset_007.phpt b/Zend/tests/str_offset_007.phpt index 3998ac401f63..09d254cac3bf 100644 --- a/Zend/tests/str_offset_007.phpt +++ b/Zend/tests/str_offset_007.phpt @@ -11,6 +11,9 @@ $a[0][$d]='b'; var_dump($a); ?> --EXPECT-- +array(1) { + [0]=> + string(1) "b" +} Err: Undefined variable $d Err: String offset cast occurred -string(0) "" diff --git a/Zend/tests/str_offset_008.phpt b/Zend/tests/str_offset_008.phpt index e99e46e59e74..7a88136d5298 100644 --- a/Zend/tests/str_offset_008.phpt +++ b/Zend/tests/str_offset_008.phpt @@ -12,7 +12,7 @@ var_dump($a[0][$b]); var_dump($a); ?> --EXPECT-- +string(1) "x" Err: Undefined variable $b Err: String offset cast occurred -string(1) "x" int(8) diff --git a/Zend/tests/strlen_deprecation_to_exception.phpt b/Zend/tests/strlen_deprecation_to_exception.phpt index 7b616f3b6432..48cba977a06a 100644 --- a/Zend/tests/strlen_deprecation_to_exception.phpt +++ b/Zend/tests/strlen_deprecation_to_exception.phpt @@ -13,5 +13,9 @@ try { } ?> ---EXPECT-- -strlen(): Passing null to parameter #1 ($string) of type string is deprecated +--EXPECTF-- +Fatal error: Uncaught Exception: strlen(): Passing null to parameter #1 ($string) of type string is deprecated in %s:%d +Stack trace: +#0 %s(%d): {closure:%s:%d}(%s) +#1 {main} + thrown in %s on line %d diff --git a/Zend/tests/temporary_cleaning/temporary_cleaning_015.phpt b/Zend/tests/temporary_cleaning/temporary_cleaning_015.phpt index f5e115faa970..8f0246dee1d6 100644 --- a/Zend/tests/temporary_cleaning/temporary_cleaning_015.phpt +++ b/Zend/tests/temporary_cleaning/temporary_cleaning_015.phpt @@ -13,5 +13,11 @@ try { } ?> DONE ---EXPECT-- -DONE +--EXPECTF-- +Array + +Fatal error: Uncaught Exception in %s:%d +Stack trace: +#0 %s(%d): {closure:%s:%d}(2, 'Array to string...', '%s', %d) +#1 {main} + thrown in %s on line %d diff --git a/Zend/tests/temporary_cleaning/temporary_cleaning_016.phpt b/Zend/tests/temporary_cleaning/temporary_cleaning_016.phpt index 8dc4c8ea3148..1a920b50d22d 100644 --- a/Zend/tests/temporary_cleaning/temporary_cleaning_016.phpt +++ b/Zend/tests/temporary_cleaning/temporary_cleaning_016.phpt @@ -13,4 +13,9 @@ try { DONE --EXPECTF-- Deprecated: Using ${expr} (variable variables) in strings is deprecated, use {${expr}} instead in %s on line %d -DONE + +Fatal error: Uncaught Exception in %s:%d +Stack trace: +#0 %s(%d): {closure:%s:%d}(2, 'Array to string...', '%s', %d) +#1 {main} + thrown in %s on line %d diff --git a/Zend/tests/type_coercion/float_to_int/non-rep-float-as-int-extra1.phpt b/Zend/tests/type_coercion/float_to_int/non-rep-float-as-int-extra1.phpt index 42b91f4a656d..47bd2c6b05bc 100644 --- a/Zend/tests/type_coercion/float_to_int/non-rep-float-as-int-extra1.phpt +++ b/Zend/tests/type_coercion/float_to_int/non-rep-float-as-int-extra1.phpt @@ -14,5 +14,5 @@ var_dump($b | 1); ?> --EXPECTF-- -Implicit conversion from float-string "1.0E+4%d" to int loses precision int(%d) +Implicit conversion from float-string "1.0E+4%d" to int loses precision diff --git a/Zend/tests/type_coercion/float_to_int/non-rep-float-as-int-extra3.phpt b/Zend/tests/type_coercion/float_to_int/non-rep-float-as-int-extra3.phpt index 0bfbeb01a2d0..5d44d8021910 100644 --- a/Zend/tests/type_coercion/float_to_int/non-rep-float-as-int-extra3.phpt +++ b/Zend/tests/type_coercion/float_to_int/non-rep-float-as-int-extra3.phpt @@ -14,5 +14,5 @@ var_dump(isset($ary[1.0E+42])); ?> --EXPECT-- +bool(true) The float 1.0E+42 is not representable as an int, cast occurred -bool(false) diff --git a/Zend/tests/type_coercion/float_to_int/non-rep-float-as-int-extra4.phpt b/Zend/tests/type_coercion/float_to_int/non-rep-float-as-int-extra4.phpt index 8134ce08d2ee..3f49fcea85ba 100644 --- a/Zend/tests/type_coercion/float_to_int/non-rep-float-as-int-extra4.phpt +++ b/Zend/tests/type_coercion/float_to_int/non-rep-float-as-int-extra4.phpt @@ -14,5 +14,5 @@ var_dump(\array_key_exists(1.0E+42, $ary)); ?> --EXPECT-- +bool(true) The float 1.0E+42 is not representable as an int, cast occurred -bool(false) diff --git a/Zend/tests/type_coercion/gh22112.phpt b/Zend/tests/type_coercion/gh22112.phpt index 84fdc393a828..dbbaf66757b9 100644 --- a/Zend/tests/type_coercion/gh22112.phpt +++ b/Zend/tests/type_coercion/gh22112.phpt @@ -31,5 +31,7 @@ try { ?> --EXPECT-- +take_bool entered bool: unexpected NAN value was coerced to bool +take_string entered string: unexpected NAN value was coerced to string diff --git a/Zend/tests/undef_index_to_exception.phpt b/Zend/tests/undef_index_to_exception.phpt index bbe13c0e71d0..5ed70b1f1494 100644 --- a/Zend/tests/undef_index_to_exception.phpt +++ b/Zend/tests/undef_index_to_exception.phpt @@ -35,12 +35,9 @@ try { } ?> ---EXPECT-- -Undefined array key 0 -array(0) { -} -Undefined array key "key" -array(0) { -} -Undefined global variable $test -Undefined variable $test +--EXPECTF-- +Fatal error: Uncaught Exception: Undefined array key 0 in %s:%d +Stack trace: +#0 %s(%d): {closure:%s:%d}(2, 'Undefined array...', '%s', %d) +#1 {main} + thrown in %s on line %d diff --git a/Zend/tests/undef_var_in_verify_return.phpt b/Zend/tests/undef_var_in_verify_return.phpt index f3966c6992d6..0a9dff20343f 100644 --- a/Zend/tests/undef_var_in_verify_return.phpt +++ b/Zend/tests/undef_var_in_verify_return.phpt @@ -15,9 +15,14 @@ test(); ?> --EXPECTF-- -Fatal error: Uncaught ErrorException: Undefined variable $test in %s:%d +Fatal error: Uncaught TypeError: test(): Return value must be of type string, null returned in %s:%d +Stack trace: +#0 %s(%d): test() +#1 {main} + +Next ErrorException: Undefined variable $test in %s:%d Stack trace: #0 %s(%d): {closure:%s:%d}(2, 'Undefined varia...', '%s', 8) #1 %s(%d): test() #2 {main} - thrown in %s on line %d + thrown in %s on line %d \ No newline at end of file diff --git a/Zend/zend.c b/Zend/zend.c index 9411b92a2018..eebb35b9cf60 100644 --- a/Zend/zend.c +++ b/Zend/zend.c @@ -1436,9 +1436,10 @@ ZEND_API zval *zend_get_configuration_directive(zend_string *name) /* {{{ */ } \ } while (0) -ZEND_API ZEND_COLD void zend_error_zstr_at( +static void zend_call_user_error_handler( int orig_type, zend_string *error_filename, uint32_t error_lineno, zend_string *message) { + int type = orig_type & E_ALL; zval params[4]; zval retval; zval orig_user_error_handler; @@ -1446,11 +1447,199 @@ ZEND_API ZEND_COLD void zend_error_zstr_at( zend_class_entry *saved_class_entry = NULL; zend_stack loop_var_stack; zend_stack delayed_oplines_stack; - int type = orig_type & E_ALL; bool orig_record_errors; zend_err_buf orig_errors_buf; zend_result res; + if (Z_TYPE(EG(user_error_handler)) == IS_UNDEF + || !(EG(user_error_handler_error_reporting) & type) + || EG(error_handling) != EH_NORMAL) { + zend_error_cb(orig_type, error_filename, error_lineno, message); + return; + } + + zend_flush_deferred_errors(); + + ZVAL_STR_COPY(¶ms[1], message); + ZVAL_LONG(¶ms[0], type); + + if (error_filename) { + ZVAL_STR_COPY(¶ms[2], error_filename); + } else { + ZVAL_NULL(¶ms[2]); + } + + ZVAL_LONG(¶ms[3], error_lineno); + + ZVAL_COPY_VALUE(&orig_user_error_handler, &EG(user_error_handler)); + ZVAL_UNDEF(&EG(user_error_handler)); + + /* User error handler may include() additional PHP files. + * If an error was generated during compilation PHP will compile + * such scripts recursively, but some CG() variables may be + * inconsistent. */ + + in_compilation = CG(in_compilation); + if (in_compilation) { + saved_class_entry = CG(active_class_entry); + CG(active_class_entry) = NULL; + SAVE_STACK(loop_var_stack); + SAVE_STACK(delayed_oplines_stack); + CG(in_compilation) = 0; + } + + orig_record_errors = EG(record_errors); + EG(record_errors) = false; + + orig_errors_buf = EG(errors); + memset(&EG(errors), 0, sizeof(EG(errors))); + + res = call_user_function(CG(function_table), NULL, &orig_user_error_handler, &retval, 4, params); + + EG(record_errors) = orig_record_errors; + EG(errors) = orig_errors_buf; + + if (res == SUCCESS) { + if (Z_TYPE(retval) != IS_UNDEF) { + if (Z_TYPE(retval) == IS_FALSE) { + zend_error_cb(orig_type, error_filename, error_lineno, message); + } + zval_ptr_dtor(&retval); + } + } else if (!EG(exception)) { + /* The user error handler failed, use built-in error handler */ + zend_error_cb(orig_type, error_filename, error_lineno, message); + } + + if (in_compilation) { + CG(active_class_entry) = saved_class_entry; + RESTORE_STACK(loop_var_stack); + RESTORE_STACK(delayed_oplines_stack); + CG(in_compilation) = 1; + } + + zval_ptr_dtor(¶ms[2]); + zval_ptr_dtor(¶ms[1]); + + if (Z_TYPE(EG(user_error_handler)) == IS_UNDEF) { + ZVAL_COPY_VALUE(&EG(user_error_handler), &orig_user_error_handler); + } else { + zval_ptr_dtor(&orig_user_error_handler); + } +} + +static zend_always_inline bool zend_can_defer_error(int type) +{ + if (type == E_USER_ERROR || type == E_RECOVERABLE_ERROR) { + return false; + } + if (CG(in_compilation) || !EG(current_execute_data) || !EG(current_execute_data)->func) { + return false; + } + zend_execute_data *ex = EG(current_execute_data); + if (!ZEND_USER_CODE(ex->func->type) || !ex->opline) { + return false; + } + switch (ex->opline->opcode) { + case ZEND_DECLARE_CLASS: + case ZEND_DECLARE_CLASS_DELAYED: + case ZEND_DECLARE_ANON_CLASS: + case ZEND_INIT_FCALL: + case ZEND_INIT_FCALL_BY_NAME: + case ZEND_INIT_NS_FCALL_BY_NAME: + case ZEND_INIT_METHOD_CALL: + case ZEND_INIT_STATIC_METHOD_CALL: + case ZEND_INIT_USER_CALL: + case ZEND_INIT_DYNAMIC_CALL: + case ZEND_INIT_PARENT_PROPERTY_HOOK_CALL: + case ZEND_DO_FCALL: + case ZEND_DO_FCALL_BY_NAME: + case ZEND_DO_UCALL: + case ZEND_FETCH_CONSTANT: + case ZEND_FETCH_CLASS_CONSTANT: + case ZEND_INCLUDE_OR_EVAL: + return false; + } + return true; +} + +static void zend_defer_error( + int orig_type, zend_string *error_filename, uint32_t error_lineno, zend_string *message) +{ + zend_error_info *info = emalloc(sizeof(zend_error_info)); + info->type = orig_type; + info->lineno = error_lineno; + info->error_reporting = EG(error_reporting); + info->filename = error_filename ? zend_string_copy(error_filename) : NULL; + info->message = zend_string_copy(message); + + EG(deferred_errors).size++; + if (EG(deferred_errors).size > EG(deferred_errors).capacity) { + uint32_t capacity = EG(deferred_errors).capacity + ? EG(deferred_errors).capacity + (EG(deferred_errors).capacity >> 1) : 2; + EG(deferred_errors).errors = erealloc(EG(deferred_errors).errors, sizeof(zend_error_info *) * capacity); + EG(deferred_errors).capacity = capacity; + } + EG(deferred_errors).errors[EG(deferred_errors).size - 1] = info; + + zend_atomic_bool_store_ex(&EG(vm_interrupt), true); +} + +ZEND_API void zend_flush_deferred_errors(void) +{ + if (!EG(deferred_errors).size) { + return; + } + + /* A user error handler cannot run while an exception is pending. Keep the + * errors buffered so ZEND_HANDLE_EXCEPTION can flush them with the + * exception cleared, instead of silently dropping them. */ + if (EG(exception)) { + return; + } + + zend_err_buf buf = EG(deferred_errors); + memset(&EG(deferred_errors), 0, sizeof(EG(deferred_errors))); + + int orig_error_reporting = EG(error_reporting); + for (uint32_t i = 0; i < buf.size; i++) { + zend_error_info *info = buf.errors[i]; + if (!EG(exception)) { + EG(error_reporting) = info->error_reporting; + zend_call_user_error_handler(info->type, info->filename, info->lineno, info->message); + } + if (info->filename) { + zend_string_release(info->filename); + } + zend_string_release(info->message); + efree_size(info, sizeof(zend_error_info)); + } + EG(error_reporting) = orig_error_reporting; + efree(buf.errors); +} + +ZEND_API void zend_free_deferred_errors(void) +{ + if (!EG(deferred_errors).size) { + return; + } + for (uint32_t i = 0; i < EG(deferred_errors).size; i++) { + zend_error_info *info = EG(deferred_errors).errors[i]; + if (info->filename) { + zend_string_release(info->filename); + } + zend_string_release(info->message); + efree_size(info, sizeof(zend_error_info)); + } + efree(EG(deferred_errors).errors); + memset(&EG(deferred_errors), 0, sizeof(EG(deferred_errors))); +} + +ZEND_API ZEND_COLD void zend_error_zstr_at( + int orig_type, zend_string *error_filename, uint32_t error_lineno, zend_string *message) +{ + int type = orig_type & E_ALL; + /* If we're executing a function during SCCP, count any warnings that may be emitted, * but don't perform any other error handling. */ if (EG(capture_warnings_during_sccp)) { @@ -1483,6 +1672,7 @@ ZEND_API ZEND_COLD void zend_error_zstr_at( zend_error_info *info = emalloc(sizeof(zend_error_info)); info->type = type; info->lineno = error_lineno; + info->error_reporting = EG(error_reporting); info->filename = zend_string_copy(error_filename); info->message = zend_string_copy(message); EG(errors).size++; @@ -1544,72 +1734,10 @@ ZEND_API ZEND_COLD void zend_error_zstr_at( zend_error_cb(orig_type, error_filename, error_lineno, message); break; default: - /* Handle the error in user space */ - ZVAL_STR_COPY(¶ms[1], message); - ZVAL_LONG(¶ms[0], type); - - if (error_filename) { - ZVAL_STR_COPY(¶ms[2], error_filename); - } else { - ZVAL_NULL(¶ms[2]); - } - - ZVAL_LONG(¶ms[3], error_lineno); - - ZVAL_COPY_VALUE(&orig_user_error_handler, &EG(user_error_handler)); - ZVAL_UNDEF(&EG(user_error_handler)); - - /* User error handler may include() additional PHP files. - * If an error was generated during compilation PHP will compile - * such scripts recursively, but some CG() variables may be - * inconsistent. */ - - in_compilation = CG(in_compilation); - if (in_compilation) { - saved_class_entry = CG(active_class_entry); - CG(active_class_entry) = NULL; - SAVE_STACK(loop_var_stack); - SAVE_STACK(delayed_oplines_stack); - CG(in_compilation) = 0; - } - - orig_record_errors = EG(record_errors); - EG(record_errors) = false; - - orig_errors_buf = EG(errors); - memset(&EG(errors), 0, sizeof(EG(errors))); - - res = call_user_function(CG(function_table), NULL, &orig_user_error_handler, &retval, 4, params); - - EG(record_errors) = orig_record_errors; - EG(errors) = orig_errors_buf; - - if (res == SUCCESS) { - if (Z_TYPE(retval) != IS_UNDEF) { - if (Z_TYPE(retval) == IS_FALSE) { - zend_error_cb(orig_type, error_filename, error_lineno, message); - } - zval_ptr_dtor(&retval); - } - } else if (!EG(exception)) { - /* The user error handler failed, use built-in error handler */ - zend_error_cb(orig_type, error_filename, error_lineno, message); - } - - if (in_compilation) { - CG(active_class_entry) = saved_class_entry; - RESTORE_STACK(loop_var_stack); - RESTORE_STACK(delayed_oplines_stack); - CG(in_compilation) = 1; - } - - zval_ptr_dtor(¶ms[2]); - zval_ptr_dtor(¶ms[1]); - - if (Z_TYPE(EG(user_error_handler)) == IS_UNDEF) { - ZVAL_COPY_VALUE(&EG(user_error_handler), &orig_user_error_handler); + if (zend_can_defer_error(type)) { + zend_defer_error(orig_type, error_filename, error_lineno, message); } else { - zval_ptr_dtor(&orig_user_error_handler); + zend_call_user_error_handler(orig_type, error_filename, error_lineno, message); } break; } @@ -1973,6 +2101,7 @@ ZEND_API zend_result zend_execute_script(int type, zval *retval, zend_file_handl zend_result ret = SUCCESS; if (op_array) { zend_execute(op_array, retval); + zend_flush_deferred_errors(); if (UNEXPECTED(EG(exception))) { if (Z_TYPE(EG(user_exception_handler)) != IS_UNDEF) { zend_user_exception_handler(); diff --git a/Zend/zend.h b/Zend/zend.h index 0d5303192b57..302f76039295 100644 --- a/Zend/zend.h +++ b/Zend/zend.h @@ -128,6 +128,7 @@ typedef struct _zend_inheritance_cache_entry zend_inheritance_cache_entry; typedef struct _zend_error_info { int type; uint32_t lineno; + int error_reporting; zend_string *filename; zend_string *message; } zend_error_info; @@ -453,6 +454,8 @@ ZEND_API void zend_begin_record_errors(void); ZEND_API void zend_emit_recorded_errors(void); ZEND_API void zend_emit_recorded_errors_ex(uint32_t num_errors, zend_error_info **errors); ZEND_API void zend_free_recorded_errors(void); +ZEND_API void zend_flush_deferred_errors(void); +ZEND_API void zend_free_deferred_errors(void); END_EXTERN_C() #define DEBUG_BACKTRACE_PROVIDE_OBJECT (1<<0) diff --git a/Zend/zend_execute.c b/Zend/zend_execute.c index 1b28ce25fe37..928bc3395e90 100644 --- a/Zend/zend_execute.c +++ b/Zend/zend_execute.c @@ -4316,13 +4316,17 @@ ZEND_API ZEND_COLD void ZEND_FASTCALL zend_fcall_interrupt(zend_execute_data *ca zend_atomic_bool_store_ex(&EG(vm_interrupt), false); if (zend_atomic_bool_load_ex(&EG(timed_out))) { zend_timeout(); - } else if (zend_interrupt_function) { - zend_interrupt_function(call); + } else { + zend_flush_deferred_errors(); + if (zend_interrupt_function) { + zend_interrupt_function(call); + } } } #define ZEND_VM_INTERRUPT_CHECK() do { \ if (UNEXPECTED(zend_atomic_bool_load_ex(&EG(vm_interrupt)))) { \ + SAVE_OPLINE(); \ ZEND_VM_INTERRUPT(); \ } \ } while (0) diff --git a/Zend/zend_execute_API.c b/Zend/zend_execute_API.c index 71e0c56a51c8..87dbe9ac6c4e 100644 --- a/Zend/zend_execute_API.c +++ b/Zend/zend_execute_API.c @@ -192,6 +192,7 @@ void init_executor(void) /* {{{ */ EG(record_errors) = false; memset(&EG(errors), 0, sizeof(EG(errors))); + memset(&EG(deferred_errors), 0, sizeof(EG(deferred_errors))); EG(filename_override) = NULL; EG(lineno_override) = -1; @@ -445,6 +446,8 @@ void shutdown_executor(void) /* {{{ */ bool fast_shutdown = is_zend_mm() && !EG(full_tables_cleanup); #endif + zend_free_deferred_errors(); + zend_try { zend_stream_shutdown(); } zend_end_try(); diff --git a/Zend/zend_globals.h b/Zend/zend_globals.h index 61499c0cc23d..20b07d12eb07 100644 --- a/Zend/zend_globals.h +++ b/Zend/zend_globals.h @@ -306,6 +306,11 @@ struct _zend_executor_globals { bool record_errors; zend_err_buf errors; + /* Diagnostics whose user error handler is deferred to the next VM + * safepoint, so the handler cannot run mid-opcode and corrupt a + * structure the opcode still holds a pointer into. */ + zend_err_buf deferred_errors; + /* Override filename or line number of thrown errors and exceptions */ zend_string *filename_override; zend_long lineno_override; diff --git a/Zend/zend_vm_def.h b/Zend/zend_vm_def.h index a0bfad488f85..9cdefe8270c5 100644 --- a/Zend/zend_vm_def.h +++ b/Zend/zend_vm_def.h @@ -2992,6 +2992,10 @@ ZEND_VM_HOT_HELPER(zend_leave_helper, ANY, ANY) SAVE_OPLINE(); #endif + if (UNEXPECTED(EG(deferred_errors).size)) { + zend_flush_deferred_errors(); + } + if (EXPECTED((call_info & (ZEND_CALL_CODE|ZEND_CALL_TOP|ZEND_CALL_HAS_SYMBOL_TABLE|ZEND_CALL_FREE_EXTRA_ARGS|ZEND_CALL_ALLOCATED|ZEND_CALL_HAS_EXTRA_NAMED_PARAMS)) == 0)) { EG(current_execute_data) = EX(prev_execute_data); i_free_compiled_variables(execute_data); @@ -8223,6 +8227,21 @@ ZEND_VM_HANDLER(149, ZEND_HANDLE_EXCEPTION, ANY, ANY) { const zend_op *throw_op = EG(opline_before_exception); + if (EG(deferred_errors).size) { + zend_object *orig_exception = EG(exception); + EX(opline) = EG(opline_before_exception); + EG(exception) = NULL; + + zend_flush_deferred_errors(); + + if (EG(exception)) { + zend_exception_set_previous(EG(exception), orig_exception); + } else { + EG(exception) = orig_exception; + EX(opline) = EG(exception_op); + } + } + /* Exception was thrown before executing any op */ if (UNEXPECTED(!throw_op)) { ZEND_VM_DISPATCH_TO_HELPER(zend_dispatch_try_catch_finally_helper, try_catch_offset, -1, op_num, 0); @@ -10635,23 +10654,44 @@ ZEND_VM_HELPER(zend_interrupt_helper, ANY, ANY) #endif if (zend_atomic_bool_load_ex(&EG(timed_out))) { zend_timeout(); - } else if (zend_interrupt_function) { - zend_interrupt_function(execute_data); - if (EG(exception)) { - /* We have to UNDEF result, because ZEND_HANDLE_EXCEPTION is going to free it */ - const zend_op *throw_op = EG(opline_before_exception); + } else { + bool entered = false; + if (EG(deferred_errors).size) { + if (EX(call) + || opline->opcode == ZEND_DO_FCALL + || opline->opcode == ZEND_DO_ICALL + || opline->opcode == ZEND_DO_UCALL + || opline->opcode == ZEND_DO_FCALL_BY_NAME) { + zend_atomic_bool_store_ex(&EG(vm_interrupt), true); + if (!zend_interrupt_function) { + ZEND_VM_CONTINUE(); + } + } else { + zend_flush_deferred_errors(); + entered = true; + } + } + if (zend_interrupt_function) { + zend_interrupt_function(execute_data); + entered = true; + } + if (entered) { + if (EG(exception)) { + /* We have to UNDEF result, because ZEND_HANDLE_EXCEPTION is going to free it */ + const zend_op *throw_op = EG(opline_before_exception); - if (throw_op - && throw_op->result_type & (IS_TMP_VAR|IS_VAR) - && throw_op->opcode != ZEND_ADD_ARRAY_ELEMENT - && throw_op->opcode != ZEND_ADD_ARRAY_UNPACK - && throw_op->opcode != ZEND_ROPE_INIT - && throw_op->opcode != ZEND_ROPE_ADD) { - ZVAL_UNDEF(ZEND_CALL_VAR(EG(current_execute_data), throw_op->result.var)); + if (throw_op + && throw_op->result_type & (IS_TMP_VAR|IS_VAR) + && throw_op->opcode != ZEND_ADD_ARRAY_ELEMENT + && throw_op->opcode != ZEND_ADD_ARRAY_UNPACK + && throw_op->opcode != ZEND_ROPE_INIT + && throw_op->opcode != ZEND_ROPE_ADD) { + ZVAL_UNDEF(ZEND_CALL_VAR(EG(current_execute_data), throw_op->result.var)); + } } + ZEND_VM_ENTER(); } - ZEND_VM_ENTER(); } ZEND_VM_CONTINUE(); } diff --git a/Zend/zend_vm_execute.h b/Zend/zend_vm_execute.h index cedc735bbb1e..72232d4ed5c6 100644 --- a/Zend/zend_vm_execute.h +++ b/Zend/zend_vm_execute.h @@ -1157,6 +1157,10 @@ static zend_never_inline ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV SAVE_OPLINE(); #endif + if (UNEXPECTED(EG(deferred_errors).size)) { + zend_flush_deferred_errors(); + } + if (EXPECTED((call_info & (ZEND_CALL_CODE|ZEND_CALL_TOP|ZEND_CALL_HAS_SYMBOL_TABLE|ZEND_CALL_FREE_EXTRA_ARGS|ZEND_CALL_ALLOCATED|ZEND_CALL_HAS_EXTRA_NAMED_PARAMS)) == 0)) { EG(current_execute_data) = EX(prev_execute_data); i_free_compiled_variables(execute_data); @@ -3399,6 +3403,21 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV ZEND_HANDLE_EXCEPT { const zend_op *throw_op = EG(opline_before_exception); + if (EG(deferred_errors).size) { + zend_object *orig_exception = EG(exception); + EX(opline) = EG(opline_before_exception); + EG(exception) = NULL; + + zend_flush_deferred_errors(); + + if (EG(exception)) { + zend_exception_set_previous(EG(exception), orig_exception); + } else { + EG(exception) = orig_exception; + EX(opline) = EG(exception_op); + } + } + /* Exception was thrown before executing any op */ if (UNEXPECTED(!throw_op)) { ZEND_VM_DISPATCH_TO_HELPER(zend_dispatch_try_catch_finally_helper_SPEC(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_EX -1, 0)); @@ -4040,23 +4059,44 @@ static zend_never_inline ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV #endif if (zend_atomic_bool_load_ex(&EG(timed_out))) { zend_timeout(); - } else if (zend_interrupt_function) { - zend_interrupt_function(execute_data); - if (EG(exception)) { - /* We have to UNDEF result, because ZEND_HANDLE_EXCEPTION is going to free it */ - const zend_op *throw_op = EG(opline_before_exception); + } else { + bool entered = false; + if (EG(deferred_errors).size) { + if (EX(call) + || opline->opcode == ZEND_DO_FCALL + || opline->opcode == ZEND_DO_ICALL + || opline->opcode == ZEND_DO_UCALL + || opline->opcode == ZEND_DO_FCALL_BY_NAME) { + zend_atomic_bool_store_ex(&EG(vm_interrupt), true); + if (!zend_interrupt_function) { + ZEND_VM_CONTINUE(); + } + } else { + zend_flush_deferred_errors(); + entered = true; + } + } + if (zend_interrupt_function) { + zend_interrupt_function(execute_data); + entered = true; + } + if (entered) { + if (EG(exception)) { + /* We have to UNDEF result, because ZEND_HANDLE_EXCEPTION is going to free it */ + const zend_op *throw_op = EG(opline_before_exception); - if (throw_op - && throw_op->result_type & (IS_TMP_VAR|IS_VAR) - && throw_op->opcode != ZEND_ADD_ARRAY_ELEMENT - && throw_op->opcode != ZEND_ADD_ARRAY_UNPACK - && throw_op->opcode != ZEND_ROPE_INIT - && throw_op->opcode != ZEND_ROPE_ADD) { - ZVAL_UNDEF(ZEND_CALL_VAR(EG(current_execute_data), throw_op->result.var)); + if (throw_op + && throw_op->result_type & (IS_TMP_VAR|IS_VAR) + && throw_op->opcode != ZEND_ADD_ARRAY_ELEMENT + && throw_op->opcode != ZEND_ADD_ARRAY_UNPACK + && throw_op->opcode != ZEND_ROPE_INIT + && throw_op->opcode != ZEND_ROPE_ADD) { + ZVAL_UNDEF(ZEND_CALL_VAR(EG(current_execute_data), throw_op->result.var)); + } } + ZEND_VM_ENTER(); } - ZEND_VM_ENTER(); } ZEND_VM_CONTINUE(); } @@ -53962,6 +54002,10 @@ static zend_never_inline ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV zend SAVE_OPLINE(); #endif + if (UNEXPECTED(EG(deferred_errors).size)) { + zend_flush_deferred_errors(); + } + if (EXPECTED((call_info & (ZEND_CALL_CODE|ZEND_CALL_TOP|ZEND_CALL_HAS_SYMBOL_TABLE|ZEND_CALL_FREE_EXTRA_ARGS|ZEND_CALL_ALLOCATED|ZEND_CALL_HAS_EXTRA_NAMED_PARAMS)) == 0)) { EG(current_execute_data) = EX(prev_execute_data); i_free_compiled_variables(execute_data); @@ -56088,6 +56132,21 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV ZEND_HANDLE_EXCEPTION_S { const zend_op *throw_op = EG(opline_before_exception); + if (EG(deferred_errors).size) { + zend_object *orig_exception = EG(exception); + EX(opline) = EG(opline_before_exception); + EG(exception) = NULL; + + zend_flush_deferred_errors(); + + if (EG(exception)) { + zend_exception_set_previous(EG(exception), orig_exception); + } else { + EG(exception) = orig_exception; + EX(opline) = EG(exception_op); + } + } + /* Exception was thrown before executing any op */ if (UNEXPECTED(!throw_op)) { ZEND_VM_DISPATCH_TO_HELPER(zend_dispatch_try_catch_finally_helper_SPEC_TAILCALL(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_EX -1, 0)); @@ -56729,23 +56788,44 @@ static zend_never_inline ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV zend #endif if (zend_atomic_bool_load_ex(&EG(timed_out))) { zend_timeout(); - } else if (zend_interrupt_function) { - zend_interrupt_function(execute_data); - if (EG(exception)) { - /* We have to UNDEF result, because ZEND_HANDLE_EXCEPTION is going to free it */ - const zend_op *throw_op = EG(opline_before_exception); + } else { + bool entered = false; + if (EG(deferred_errors).size) { + if (EX(call) + || opline->opcode == ZEND_DO_FCALL + || opline->opcode == ZEND_DO_ICALL + || opline->opcode == ZEND_DO_UCALL + || opline->opcode == ZEND_DO_FCALL_BY_NAME) { + zend_atomic_bool_store_ex(&EG(vm_interrupt), true); + if (!zend_interrupt_function) { + ZEND_VM_CONTINUE(); + } + } else { + zend_flush_deferred_errors(); + entered = true; + } + } + if (zend_interrupt_function) { + zend_interrupt_function(execute_data); + entered = true; + } + if (entered) { + if (EG(exception)) { + /* We have to UNDEF result, because ZEND_HANDLE_EXCEPTION is going to free it */ + const zend_op *throw_op = EG(opline_before_exception); - if (throw_op - && throw_op->result_type & (IS_TMP_VAR|IS_VAR) - && throw_op->opcode != ZEND_ADD_ARRAY_ELEMENT - && throw_op->opcode != ZEND_ADD_ARRAY_UNPACK - && throw_op->opcode != ZEND_ROPE_INIT - && throw_op->opcode != ZEND_ROPE_ADD) { - ZVAL_UNDEF(ZEND_CALL_VAR(EG(current_execute_data), throw_op->result.var)); + if (throw_op + && throw_op->result_type & (IS_TMP_VAR|IS_VAR) + && throw_op->opcode != ZEND_ADD_ARRAY_ELEMENT + && throw_op->opcode != ZEND_ADD_ARRAY_UNPACK + && throw_op->opcode != ZEND_ROPE_INIT + && throw_op->opcode != ZEND_ROPE_ADD) { + ZVAL_UNDEF(ZEND_CALL_VAR(EG(current_execute_data), throw_op->result.var)); + } } + ZEND_VM_ENTER(); } - ZEND_VM_ENTER(); } ZEND_VM_CONTINUE(); } @@ -110304,6 +110384,10 @@ ZEND_API void execute_ex(zend_execute_data *ex) SAVE_OPLINE(); #endif + if (UNEXPECTED(EG(deferred_errors).size)) { + zend_flush_deferred_errors(); + } + if (EXPECTED((call_info & (ZEND_CALL_CODE|ZEND_CALL_TOP|ZEND_CALL_HAS_SYMBOL_TABLE|ZEND_CALL_FREE_EXTRA_ARGS|ZEND_CALL_ALLOCATED|ZEND_CALL_HAS_EXTRA_NAMED_PARAMS)) == 0)) { EG(current_execute_data) = EX(prev_execute_data); i_free_compiled_variables(execute_data); diff --git a/ext/opcache/jit/zend_jit.c b/ext/opcache/jit/zend_jit.c index fbbfab6b243c..2c47d2690f62 100644 --- a/ext/opcache/jit/zend_jit.c +++ b/ext/opcache/jit/zend_jit.c @@ -1425,6 +1425,7 @@ static int zend_jit(const zend_op_array *op_array, zend_ssa *ssa, const zend_op int call_level = 0; void *checkpoint = NULL; bool recv_emitted = false; /* emitted at least one RECV opcode */ + bool entry_flush_emitted = false; uint8_t smart_branch_opcode; uint32_t target_label, target_label2; uint32_t op1_info, op1_def_info, op2_info, res_info, res_use_info, op1_mem_info; @@ -1574,7 +1575,7 @@ static int zend_jit(const zend_op_array *op_array, zend_ssa *ssa, const zend_op zend_jit_set_last_valid_opline(&ctx, op_array->opcodes + ssa->cfg.blocks[b].start); } if (ssa->cfg.blocks[b].flags & ZEND_BB_LOOP_HEADER) { - zend_jit_check_timeout(&ctx, op_array->opcodes + ssa->cfg.blocks[b].start, NULL); + zend_jit_check_loop_timeout(&ctx, op_array->opcodes + ssa->cfg.blocks[b].start); } if (!ssa->cfg.blocks[b].len) { zend_jit_bb_end(&ctx, b); @@ -1604,6 +1605,13 @@ static int zend_jit(const zend_op_array *op_array, zend_ssa *ssa, const zend_op phi = phi->next; } } + if ((ssa->cfg.blocks[b].flags & ZEND_BB_TARGET) + && ssa->cfg.blocks[b].start != 0 + && !(ssa->cfg.blocks[b].flags & ZEND_BB_LOOP_HEADER)) { + if (!zend_jit_deferred_error_deopt(&ctx, op_array, ssa, b, op_array->opcodes + ssa->cfg.blocks[b].start, ssa->cfg.blocks[b].start)) { + goto jit_failure; + } + } end = ssa->cfg.blocks[b].start + ssa->cfg.blocks[b].len - 1; for (i = ssa->cfg.blocks[b].start; i <= end; i++) { zend_ssa_op *ssa_op = ssa->ops ? &ssa->ops[i] : NULL; @@ -1612,6 +1620,13 @@ static int zend_jit(const zend_op_array *op_array, zend_ssa *ssa, const zend_op call_level++; } + if (!entry_flush_emitted) { + entry_flush_emitted = true; + if (!zend_jit_deferred_error_deopt(&ctx, op_array, ssa, b, opline, i)) { + goto jit_failure; + } + } + if (JIT_G(opt_level) >= ZEND_JIT_LEVEL_INLINE) { switch (opline->opcode) { case ZEND_PRE_INC: @@ -2934,7 +2949,7 @@ static int zend_jit(const zend_op_array *op_array, zend_ssa *ssa, const zend_op } } if (!zend_jit_leave_func(&ctx, op_array, NULL, MAY_BE_ANY, left_frame, - NULL, NULL, (ssa->cfg.flags & ZEND_FUNC_INDIRECT_VAR_ACCESS) != 0, 1)) { + NULL, NULL, (ssa->cfg.flags & ZEND_FUNC_INDIRECT_VAR_ACCESS) != 0)) { goto jit_failure; } } diff --git a/ext/opcache/jit/zend_jit_helpers.c b/ext/opcache/jit/zend_jit_helpers.c index 64a48068f378..2b7d03ad11ea 100644 --- a/ext/opcache/jit/zend_jit_helpers.c +++ b/ext/opcache/jit/zend_jit_helpers.c @@ -3437,6 +3437,15 @@ static void ZEND_FASTCALL zend_jit_exception_in_interrupt_handler_helper(void) } } +static void ZEND_FASTCALL zend_jit_check_deferred_errors(zend_execute_data *execute_data) +{ + if (EG(deferred_errors).size && execute_data->call) { + zend_atomic_bool_store_ex(&EG(vm_interrupt), true); + } else { + zend_flush_deferred_errors(); + } +} + static zend_string* ZEND_FASTCALL zend_jit_rope_end(zend_string **rope, uint32_t count) { zend_string *ret; diff --git a/ext/opcache/jit/zend_jit_ir.c b/ext/opcache/jit/zend_jit_ir.c index 2fe0b1896a92..ab573586aba7 100644 --- a/ext/opcache/jit/zend_jit_ir.c +++ b/ext/opcache/jit/zend_jit_ir.c @@ -1944,6 +1944,32 @@ static void zend_jit_check_timeout(zend_jit_ctx *jit, const zend_op *opline, con } } +static void zend_jit_check_loop_timeout(zend_jit_ctx *jit, const zend_op *opline) +{ + ir_ref if_interrupt, if_timeout, if_deferred, ride_path, cont_path; + + if_interrupt = ir_IF(ir_LOAD_U8(jit_EG(vm_interrupt))); + ir_IF_TRUE_cold(if_interrupt); + + if_timeout = ir_IF(ir_LOAD_U8(jit_EG(timed_out))); + ir_IF_TRUE(if_timeout); + jit_LOAD_IP_ADDR(jit, opline); + ir_IJMP(jit_STUB_ADDR(jit, jit_stub_interrupt_handler)); + ir_IF_FALSE(if_timeout); + + if_deferred = ir_IF(ir_LOAD_U32(jit_EG(deferred_errors.size))); + ir_IF_FALSE(if_deferred); + jit_LOAD_IP_ADDR(jit, opline); + ir_IJMP(jit_STUB_ADDR(jit, jit_stub_interrupt_handler)); + ir_IF_TRUE(if_deferred); + ride_path = ir_END(); + + ir_IF_FALSE(if_interrupt); + cont_path = ir_END(); + + ir_MERGE_2(ride_path, cont_path); +} + static void zend_jit_vm_enter(zend_jit_ctx *jit, ir_ref to_opline) { // ZEND_VM_ENTER() @@ -2073,16 +2099,17 @@ static int zend_jit_interrupt_handler_stub(zend_jit_ctx *jit) ir_CALL(IR_VOID, ir_CONST_FUNC(zend_timeout)); ir_MERGE_WITH_EMPTY_TRUE(if_timeout); + ir_CALL_1(IR_VOID, ir_CONST_FUNC(zend_jit_check_deferred_errors), jit_FP(jit)); if (zend_interrupt_function) { ir_CALL_1(IR_VOID, ir_CONST_FUNC(zend_interrupt_function), jit_FP(jit)); - if_exception = ir_IF(ir_LOAD_A(jit_EG(exception))); - ir_IF_TRUE(if_exception); - ir_CALL(IR_VOID, ir_CONST_FUNC(zend_jit_exception_in_interrupt_handler_helper)); - ir_MERGE_WITH_EMPTY_FALSE(if_exception); - - jit_STORE_FP(jit, ir_LOAD_A(jit_EG(current_execute_data))); - jit_STORE_IP(jit, ir_LOAD_A(jit_EX(opline))); } + if_exception = ir_IF(ir_LOAD_A(jit_EG(exception))); + ir_IF_TRUE(if_exception); + ir_CALL(IR_VOID, ir_CONST_FUNC(zend_jit_exception_in_interrupt_handler_helper)); + ir_MERGE_WITH_EMPTY_FALSE(if_exception); + + jit_STORE_FP(jit, ir_LOAD_A(jit_EG(current_execute_data))); + jit_STORE_IP(jit, ir_LOAD_A(jit_EX(opline))); if (GCC_GLOBAL_REGS || ZEND_VM_KIND == ZEND_VM_KIND_TAILCALL) { zend_jit_tailcall_handler(jit, ir_LOAD_A(jit_IP(jit))); @@ -2528,9 +2555,6 @@ static int zend_jit_trace_exit_stub(zend_jit_ctx *jit) ref = ir_LOAD_A(jit_EX(opline)); jit_STORE_IP(jit, ref); - // check for interrupt (try to avoid this ???) - zend_jit_check_timeout(jit, NULL, NULL); - addr = zend_jit_orig_opline_handler(jit); if (GCC_GLOBAL_REGS || ZEND_VM_KIND == ZEND_VM_KIND_TAILCALL) { zend_jit_tailcall_handler(jit, addr); @@ -3132,6 +3156,7 @@ static void zend_jit_setup_disasm(void) REGISTER_HELPER(zend_free_extra_named_params); REGISTER_HELPER(zend_jit_free_call_frame); REGISTER_HELPER(zend_jit_exception_in_interrupt_handler_helper); + REGISTER_HELPER(zend_jit_check_deferred_errors); REGISTER_HELPER(zend_jit_verify_arg_slow); REGISTER_HELPER(zend_missing_arg_error); REGISTER_HELPER(zend_jit_only_vars_by_reference); @@ -4339,6 +4364,68 @@ static int zend_jit_store_var(zend_jit_ctx *jit, uint32_t info, int var, int ssa return zend_jit_spill_store(jit, src, dst, info, set_type); } +static bool zend_jit_ssa_var_live_at(const zend_ssa *ssa, int v, uint32_t i) +{ + int use = ssa->vars[v].use_chain; + + while (use >= 0) { + if ((uint32_t)use >= i) { + return 1; + } + use = zend_ssa_next_use(ssa->ops, v, use); + } + return 0; +} + +static int zend_jit_materialize_live_vars(zend_jit_ctx *jit, const zend_op_array *op_array, zend_ssa *ssa, int b, uint32_t i) +{ + int v; + zend_ssa_phi *phi; + + if (!jit->ra) { + return 1; + } + phi = ssa->blocks[b].phis; + while (phi) { + v = phi->ssa_var; + if (phi->pi < 0 + && jit->ra[v].ref + && !(jit->ra[v].flags & ZREG_LOAD)) { + if (!zend_jit_store_var(jit, ssa->var_info[v].type, ssa->vars[v].var, v, 1)) { + return 0; + } + } + phi = phi->next; + } + for (v = 0; v < ssa->vars_count; v++) { + if (jit->ra[v].ref + && !(jit->ra[v].flags & ZREG_LOAD) + && ssa->vars[v].definition >= 0 + && (uint32_t)ssa->vars[v].definition < i + && zend_jit_ssa_var_live_at(ssa, v, i)) { + if (!zend_jit_store_var(jit, ssa->var_info[v].type, ssa->vars[v].var, v, 1)) { + return 0; + } + } + } + return 1; +} + +static int zend_jit_deferred_error_deopt(zend_jit_ctx *jit, const zend_op_array *op_array, zend_ssa *ssa, int b, const zend_op *opline, uint32_t i) +{ + ir_ref if_deferred = ir_IF(ir_LOAD_U32(jit_EG(deferred_errors.size))); + + ir_IF_TRUE_cold(if_deferred); + if (!zend_jit_materialize_live_vars(jit, op_array, ssa, b, i)) { + return 0; + } + ir_STORE(jit_EG(current_execute_data), jit_FP(jit)); + jit_LOAD_IP_ADDR(jit, opline); + ir_IJMP(jit_STUB_ADDR(jit, jit_stub_interrupt_handler)); + ir_IF_FALSE(if_deferred); + return 1; +} + static int zend_jit_store_ref(zend_jit_ctx *jit, uint32_t info, int var, int32_t src, bool set_type) { zend_jit_addr dst = ZEND_ADDR_MEM_ZVAL(ZREG_FP, EX_NUM_TO_VAR(var)); @@ -10597,7 +10684,7 @@ static int zend_jit_do_fcall(zend_jit_ctx *jit, const zend_op *opline, const zen * doing it later once it's popped off. There is code further * down that handles when there isn't an interrupt function. */ - if (zend_interrupt_function) { + if (zend_interrupt_function || !trace) { // JIT: if (EG(vm_interrupt)) zend_fcall_interrupt(execute_data); ir_ref if_interrupt = ir_IF(ir_LOAD_U8(jit_EG(vm_interrupt))); ir_IF_TRUE_cold(if_interrupt); @@ -10718,9 +10805,9 @@ static int zend_jit_do_fcall(zend_jit_ctx *jit, const zend_op *opline, const zen /* If there isn't a zend_interrupt_function, the timeout is * handled here because it's more efficient. */ - if (!zend_interrupt_function) { + if (!zend_interrupt_function && trace) { // TODO: Can we avoid checking for interrupts after each call ??? - if (trace && jit->last_valid_opline != opline) { + if (trace) { int32_t exit_point = zend_jit_trace_get_exit_point(opline + 1, ZEND_JIT_EXIT_TO_VM); exit_addr = zend_jit_trace_get_exit_addr(exit_point); @@ -11061,8 +11148,7 @@ static int zend_jit_leave_func(zend_jit_ctx *jit, bool left_frame, zend_jit_trace_rec *trace, zend_jit_trace_info *trace_info, - int indirect_var_access, - int may_throw) + int indirect_var_access) { bool may_be_top_frame = JIT_G(trigger) != ZEND_JIT_ON_HOT_TRACE || @@ -11086,6 +11172,17 @@ static int zend_jit_leave_func(zend_jit_ctx *jit, !TRACE_FRAME_NO_NEED_RELEASE_THIS(JIT_G(current_frame))); ir_ref call_info = IR_UNUSED, ref, cold_path = IR_UNUSED; + { + ir_ref if_deferred = ir_IF(ir_LOAD_U32(jit_EG(deferred_errors.size))); + ir_IF_TRUE_cold(if_deferred); + ir_ref saved_ced = ir_LOAD_A(jit_EG(current_execute_data)); + ir_STORE(jit_EX(opline), jit_IP(jit)); + ir_STORE(jit_EG(current_execute_data), jit_FP(jit)); + ir_CALL(IR_VOID, ir_CONST_FUNC(zend_flush_deferred_errors)); + ir_STORE(jit_EG(current_execute_data), saved_ced); + ir_MERGE_WITH_EMPTY_FALSE(if_deferred); + } + if (may_need_call_helper) { if (!left_frame) { left_frame = true; @@ -11167,8 +11264,6 @@ static int zend_jit_leave_func(zend_jit_ctx *jit, if (fast_path) { ir_MERGE_WITH(fast_path); } - // TODO: avoid EG(excption) check for $this->foo() calls - may_throw = 1; } // JIT: EG(vm_stack_top) = (zval*)execute_data @@ -11211,13 +11306,8 @@ static int zend_jit_leave_func(zend_jit_ctx *jit, && (!JIT_G(current_frame) || TRACE_FRAME_IS_UNKNOWN_RETURN(JIT_G(current_frame)))) { const zend_op *next_opline = trace->opline; - if ((opline->op1_type & (IS_VAR|IS_TMP_VAR)) - && (op1_info & MAY_BE_RC1) - && (op1_info & (MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_ARRAY_OF_OBJECT|MAY_BE_ARRAY_OF_RESOURCE|MAY_BE_ARRAY_OF_ARRAY))) { - /* exception might be thrown during destruction of unused return value */ - // JIT: if (EG(exception)) - ir_GUARD_NOT(ir_LOAD_A(jit_EG(exception)), jit_STUB_ADDR(jit, jit_stub_leave_throw)); - } + // JIT: if (EG(exception)) + ir_GUARD_NOT(ir_LOAD_A(jit_EG(exception)), jit_STUB_ADDR(jit, jit_stub_leave_throw)); do { trace++; } while (trace->op == ZEND_JIT_TRACE_INIT_CALL); @@ -11249,11 +11339,7 @@ static int zend_jit_leave_func(zend_jit_ctx *jit, zend_jit_set_last_valid_opline(jit, trace->opline); return 1; - } else if (may_throw || - (((opline->op1_type & (IS_VAR|IS_TMP_VAR)) - && (op1_info & MAY_BE_RC1) - && (op1_info & (MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_ARRAY_OF_OBJECT|MAY_BE_ARRAY_OF_RESOURCE|MAY_BE_ARRAY_OF_ARRAY))) - && (!JIT_G(current_frame) || TRACE_FRAME_IS_RETURN_VALUE_UNUSED(JIT_G(current_frame))))) { + } else { // JIT: if (EG(exception)) ir_GUARD_NOT(ir_LOAD_A(jit_EG(exception)), jit_STUB_ADDR(jit, jit_stub_leave_throw)); } diff --git a/ext/opcache/jit/zend_jit_trace.c b/ext/opcache/jit/zend_jit_trace.c index 024a5d0e194d..81333f427bbd 100644 --- a/ext/opcache/jit/zend_jit_trace.c +++ b/ext/opcache/jit/zend_jit_trace.c @@ -5596,7 +5596,6 @@ static zend_vm_opcode_handler_t zend_jit_trace(zend_jit_trace_rec *trace_buffer, } } else { int j; - int may_throw = 0; bool left_frame = 0; if (!zend_jit_return(&ctx, opline, op_array, @@ -5638,17 +5637,12 @@ static zend_vm_opcode_handler_t zend_jit_trace(zend_jit_trace_rec *trace_buffer, if (!zend_jit_free_cv(&ctx, info, j)) { goto jit_failure; } - if (info & (MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_ARRAY_OF_OBJECT|MAY_BE_ARRAY_OF_ARRAY|MAY_BE_ARRAY_OF_RESOURCE)) { - if (info & MAY_BE_RC1) { - may_throw = 1; - } - } } } } if (!zend_jit_leave_func(&ctx, op_array, opline, op1_info, left_frame, p + 1, &zend_jit_traces[ZEND_JIT_TRACE_NUM], - (op_array_ssa->cfg.flags & ZEND_FUNC_INDIRECT_VAR_ACCESS) != 0, may_throw)) { + (op_array_ssa->cfg.flags & ZEND_FUNC_INDIRECT_VAR_ACCESS) != 0)) { goto jit_failure; } } diff --git a/ext/opcache/jit/zend_jit_vm_helpers.c b/ext/opcache/jit/zend_jit_vm_helpers.c index 271d923598d9..c5474b3345ca 100644 --- a/ext/opcache/jit/zend_jit_vm_helpers.c +++ b/ext/opcache/jit/zend_jit_vm_helpers.c @@ -102,6 +102,11 @@ ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_jit_leave_nested_func_helper(ZEND_OPC { zend_execute_data *old_execute_data; + if (UNEXPECTED(EG(deferred_errors).size)) { + EX(opline) = opline; + zend_flush_deferred_errors(); + } + if (UNEXPECTED(call_info & ZEND_CALL_HAS_SYMBOL_TABLE)) { zend_clean_and_cache_symbol_table(EX(symbol_table)); } @@ -1114,6 +1119,14 @@ zend_jit_trace_stop ZEND_FASTCALL zend_jit_trace_execute(zend_execute_data *ex, if (execute_data->prev_execute_data == prev_execute_data) { /* Enter into function */ + if (UNEXPECTED(EG(deferred_errors).size)) { + EX(opline) = opline; + zend_flush_deferred_errors(); + if (UNEXPECTED(EG(exception))) { + stop = ZEND_JIT_TRACE_STOP_EXCEPTION; + break; + } + } prev_call = NULL; if (level > ZEND_JIT_TRACE_MAX_CALL_DEPTH) { stop = ZEND_JIT_TRACE_STOP_TOO_DEEP; diff --git a/ext/opcache/tests/jit/assign_043.phpt b/ext/opcache/tests/jit/assign_043.phpt index edb0cb212ab0..6c86b94e545f 100644 --- a/ext/opcache/tests/jit/assign_043.phpt +++ b/ext/opcache/tests/jit/assign_043.phpt @@ -17,5 +17,5 @@ try { echo $e->getMessage(), "\n"; } ?> ---EXPECT-- -Undefined variable $b +--EXPECTF-- +Undefined variable $b \ No newline at end of file diff --git a/ext/opcache/tests/jit/assign_dim_005.phpt b/ext/opcache/tests/jit/assign_dim_005.phpt index e7bfdabacc4f..b839739574bb 100644 --- a/ext/opcache/tests/jit/assign_dim_005.phpt +++ b/ext/opcache/tests/jit/assign_dim_005.phpt @@ -16,6 +16,11 @@ $a[$c] = 'x' ; var_dump($a); ?> --EXPECT-- +array(1) { + [""]=> + string(1) "x" +} Error: Undefined variable $c +Error: Using null as an array offset is deprecated, use an empty string instead Error: Undefined variable $c -NULL +Error: Using null as an array offset is deprecated, use an empty string instead diff --git a/ext/opcache/tests/jit/assign_dim_007.phpt b/ext/opcache/tests/jit/assign_dim_007.phpt index 0df4e291ae93..590ab14b6670 100644 --- a/ext/opcache/tests/jit/assign_dim_007.phpt +++ b/ext/opcache/tests/jit/assign_dim_007.phpt @@ -18,7 +18,9 @@ x($y); var_dump($x,$y); ?> --EXPECT-- -array(0) { +array(1) { + [0]=> + int(1) } array(1) { [0]=> diff --git a/ext/opcache/tests/jit/assign_dim_011.phpt b/ext/opcache/tests/jit/assign_dim_011.phpt index 5df415c89e6c..daad27634196 100644 --- a/ext/opcache/tests/jit/assign_dim_011.phpt +++ b/ext/opcache/tests/jit/assign_dim_011.phpt @@ -18,7 +18,11 @@ try { } ?> DONE ---EXPECT-- +--EXPECTF-- Err: Automatic conversion of false to array is deprecated -Exception: Cannot use a scalar value as an array -DONE + +Fatal error: Uncaught Error: Cannot use a scalar value as an array in %s:%d +Stack trace: +#0 %s(%d): {closure:%s:%d}(8192, 'Automatic conve...', '%s', %d) +#1 {main} + thrown in %s on line %d diff --git a/ext/opcache/tests/jit/assign_dim_014.phpt b/ext/opcache/tests/jit/assign_dim_014.phpt index 574b05258421..45958d82edab 100644 --- a/ext/opcache/tests/jit/assign_dim_014.phpt +++ b/ext/opcache/tests/jit/assign_dim_014.phpt @@ -14,5 +14,6 @@ $a[$y] = function(){}; ?> DONE --EXPECT-- -Error: Undefined variable $y DONE +Error: Undefined variable $y +Error: Using null as an array offset is deprecated, use an empty string instead diff --git a/ext/opcache/tests/jit/assign_dim_015.phpt b/ext/opcache/tests/jit/assign_dim_015.phpt index 2a5b6d46d37a..3cfb75a31b74 100644 --- a/ext/opcache/tests/jit/assign_dim_015.phpt +++ b/ext/opcache/tests/jit/assign_dim_015.phpt @@ -14,5 +14,5 @@ $my_var[] = $y; ?> DONE --EXPECT-- -Error: Undefined variable $y DONE +Error: Undefined variable $y diff --git a/ext/opcache/tests/jit/assign_dim_op_006.phpt b/ext/opcache/tests/jit/assign_dim_op_006.phpt index 8bfb9197177a..b2b606245be8 100644 --- a/ext/opcache/tests/jit/assign_dim_op_006.phpt +++ b/ext/opcache/tests/jit/assign_dim_op_006.phpt @@ -15,4 +15,7 @@ $my_var[000000000000000000000100000000000000000000000000000000000000000000000000 var_dump($my_var); ?> --EXPECT-- -int(0) +array(1) { + [0]=> + string(3) "xyz" +} diff --git a/ext/opcache/tests/jit/fetch_dim_rw_004.phpt b/ext/opcache/tests/jit/fetch_dim_rw_004.phpt index ae639c8f56ba..b0f18f9746c5 100644 --- a/ext/opcache/tests/jit/fetch_dim_rw_004.phpt +++ b/ext/opcache/tests/jit/fetch_dim_rw_004.phpt @@ -12,12 +12,12 @@ $k=[]; $y[$k]++; ?> --EXPECTF-- -Fatal error: Uncaught TypeError: {closure:%s:%d}(): Argument #1 ($y) must be of type y, int given, called in %s on line %d and defined in %s:%d +Fatal error: Uncaught TypeError: Cannot access offset of type array on array in %s:%d Stack trace: -#0 %s(%d): {closure:%s:%d}(2, 'Undefined varia...', '%s', 5) -#1 {main} +#0 {main} -Next TypeError: Cannot access offset of type array on array in %sfetch_dim_rw_004.php:5 +Next TypeError: {closure:%s:%d}(): Argument #1 ($y) must be of type y, int given, called in %s on line %d and defined in %s:%d Stack trace: -#0 {main} - thrown in %sfetch_dim_rw_004.php on line 5 +#0 %s(%d): {closure:%s:%d}(2, 'Undefined varia...', '%s', 5) +#1 {main} + thrown in %s on line %d \ No newline at end of file diff --git a/ext/opcache/tests/jit/gh18262-002.phpt b/ext/opcache/tests/jit/gh18262-002.phpt index b82723abaa29..c4de9ff0375a 100644 --- a/ext/opcache/tests/jit/gh18262-002.phpt +++ b/ext/opcache/tests/jit/gh18262-002.phpt @@ -26,9 +26,11 @@ foreach ($tests as $obj) { ?> --EXPECTF-- int(1) +NULL Fatal error: Uncaught Exception: Undefined property: B::$b in %s:%d Stack trace: -#0 %s(%d): {closure:%s:%d}(2, 'Undefined prope...', '%s', %d) -#1 {main} +#0 [internal function]: {closure:%s:%d}(2, 'Undefined prope...', '%s', %d) +#1 %s(%d): var_dump(NULL) +#2 {main} thrown in %s on line %d diff --git a/ext/opcache/tests/jit/gh18262-003.phpt b/ext/opcache/tests/jit/gh18262-003.phpt index a3c3e037632f..9f3f8c57ad55 100644 --- a/ext/opcache/tests/jit/gh18262-003.phpt +++ b/ext/opcache/tests/jit/gh18262-003.phpt @@ -32,4 +32,5 @@ foreach ($tests as $obj) { --EXPECT-- int(1) string(3) "str" +NULL Exception: Undefined property: B::$b diff --git a/ext/opcache/tests/jit/qm_assign_undef_exception.phpt b/ext/opcache/tests/jit/qm_assign_undef_exception.phpt index 4e93e2ed7e56..45de4de6763b 100644 --- a/ext/opcache/tests/jit/qm_assign_undef_exception.phpt +++ b/ext/opcache/tests/jit/qm_assign_undef_exception.phpt @@ -17,5 +17,5 @@ try { echo $e->getMessage(), "\n"; } ?> ---EXPECT-- -Undefined variable $b +--EXPECTF-- +Undefined variable $b \ No newline at end of file diff --git a/ext/opcache/tests/jit/type_check_001.phpt b/ext/opcache/tests/jit/type_check_001.phpt index 028d2e8637e2..78936f197d5a 100644 --- a/ext/opcache/tests/jit/type_check_001.phpt +++ b/ext/opcache/tests/jit/type_check_001.phpt @@ -20,5 +20,5 @@ try { echo "Exception: " . $e->getMessage() . "\n"; } ?> ---EXPECT-- -Exception: Undefined variable $a +--EXPECTF-- +Exception: Undefined variable $a \ No newline at end of file diff --git a/ext/spl/tests/gh18274.phpt b/ext/spl/tests/gh18274.phpt new file mode 100644 index 000000000000..bc4f017d2c3b --- /dev/null +++ b/ext/spl/tests/gh18274.phpt @@ -0,0 +1,20 @@ +--TEST-- +GH-18274 (Assertion failure in SplFixedArray when an error handler runs during an undefined-variable write) +--FILE-- + +--EXPECTF-- +Fatal error: Uncaught ArgumentCountError: func_get_args() expects exactly 0 arguments, 4 given in %s:%d +Stack trace: +#%d %A: func_get_args(%d, %s, %s, %d)%A +#%d {main} + thrown in %s on line %d diff --git a/ext/standard/array.c b/ext/standard/array.c index 3b17ca3f7942..102ddbe79d91 100644 --- a/ext/standard/array.c +++ b/ext/standard/array.c @@ -680,19 +680,20 @@ PHP_FUNCTION(natcasesort) typedef bucket_compare_func_t(*get_compare_function)(zend_long); static zend_always_inline void php_sort(INTERNAL_FUNCTION_PARAMETERS, get_compare_function get_cmp, bool renumber) { - HashTable *array; + zval *array; zend_long sort_type = PHP_SORT_REGULAR; bucket_compare_func_t cmp; ZEND_PARSE_PARAMETERS_START(1, 2) - Z_PARAM_ARRAY_HT_EX(array, 0, 1) + Z_PARAM_ARRAY_EX(array, 0, 1) Z_PARAM_OPTIONAL Z_PARAM_LONG(sort_type) ZEND_PARSE_PARAMETERS_END(); cmp = get_cmp(sort_type); - zend_array_sort(array, cmp, renumber); + SEPARATE_ARRAY(array); + zend_array_sort(Z_ARRVAL_P(array), cmp, renumber); RETURN_TRUE; } @@ -911,22 +912,25 @@ PHP_FUNCTION(uksort) } /* }}} */ -static inline HashTable *get_ht_for_iap(zval *zv, bool separate) { +static inline HashTable *get_ht_for_iap(zval *zv, bool separate, zend_object **objp) { + *objp = NULL; if (EXPECTED(Z_TYPE_P(zv) == IS_ARRAY)) { return Z_ARRVAL_P(zv); } ZEND_ASSERT(Z_TYPE_P(zv) == IS_OBJECT); + zend_object *zobj = Z_OBJ_P(zv); + GC_ADDREF(zobj); php_error_docref(NULL, E_DEPRECATED, "Calling %s() on an object is deprecated", get_active_function_name()); - zend_object *zobj = Z_OBJ_P(zv); if (separate && zobj->properties && UNEXPECTED(GC_REFCOUNT(zobj->properties) > 1)) { if (EXPECTED(!(GC_FLAGS(zobj->properties) & IS_ARRAY_IMMUTABLE))) { GC_DELREF(zobj->properties); } zobj->properties = zend_array_dup(zobj->properties); } + *objp = zobj; return zobj->handlers->get_properties(zobj); } @@ -979,15 +983,19 @@ PHP_FUNCTION(end) Z_PARAM_ARRAY_OR_OBJECT_EX(array_zv, 0, 1) ZEND_PARSE_PARAMETERS_END(); - HashTable *array = get_ht_for_iap(array_zv, /* separate */ true); - if (zend_hash_num_elements(array) == 0) { + zend_object *iap_obj; + HashTable *array = get_ht_for_iap(array_zv, /* separate */ true, &iap_obj); + if (zend_hash_num_elements(array) != 0) { + zend_hash_internal_pointer_end(array); + if (USED_RET()) { + php_array_iter_return_current(return_value, array, false); + } + } else { /* array->nInternalPointer is already 0 if the array is empty, even after removing elements */ - RETURN_FALSE; + RETVAL_FALSE; } - zend_hash_internal_pointer_end(array); - - if (USED_RET()) { - php_array_iter_return_current(return_value, array, false); + if (iap_obj) { + OBJ_RELEASE(iap_obj); } } /* }}} */ @@ -1001,15 +1009,19 @@ PHP_FUNCTION(prev) Z_PARAM_ARRAY_OR_OBJECT_EX(array_zv, 0, 1) ZEND_PARSE_PARAMETERS_END(); - HashTable *array = get_ht_for_iap(array_zv, /* separate */ true); - if (zend_hash_num_elements(array) == 0) { + zend_object *iap_obj; + HashTable *array = get_ht_for_iap(array_zv, /* separate */ true, &iap_obj); + if (zend_hash_num_elements(array) != 0) { + zend_hash_move_backwards(array); + if (USED_RET()) { + php_array_iter_return_current(return_value, array, false); + } + } else { /* array->nInternalPointer is already 0 if the array is empty, even after removing elements */ - RETURN_FALSE; + RETVAL_FALSE; } - zend_hash_move_backwards(array); - - if (USED_RET()) { - php_array_iter_return_current(return_value, array, false); + if (iap_obj) { + OBJ_RELEASE(iap_obj); } } /* }}} */ @@ -1023,15 +1035,19 @@ PHP_FUNCTION(next) Z_PARAM_ARRAY_OR_OBJECT_EX(array_zv, 0, 1) ZEND_PARSE_PARAMETERS_END(); - HashTable *array = get_ht_for_iap(array_zv, /* separate */ true); - if (zend_hash_num_elements(array) == 0) { + zend_object *iap_obj; + HashTable *array = get_ht_for_iap(array_zv, /* separate */ true, &iap_obj); + if (zend_hash_num_elements(array) != 0) { + zend_hash_move_forward(array); + if (USED_RET()) { + php_array_iter_return_current(return_value, array, true); + } + } else { /* array->nInternalPointer is already 0 if the array is empty, even after removing elements */ - RETURN_FALSE; + RETVAL_FALSE; } - zend_hash_move_forward(array); - - if (USED_RET()) { - php_array_iter_return_current(return_value, array, true); + if (iap_obj) { + OBJ_RELEASE(iap_obj); } } /* }}} */ @@ -1045,15 +1061,19 @@ PHP_FUNCTION(reset) Z_PARAM_ARRAY_OR_OBJECT_EX(array_zv, 0, 1) ZEND_PARSE_PARAMETERS_END(); - HashTable *array = get_ht_for_iap(array_zv, /* separate */ true); - if (zend_hash_num_elements(array) == 0) { + zend_object *iap_obj; + HashTable *array = get_ht_for_iap(array_zv, /* separate */ true, &iap_obj); + if (zend_hash_num_elements(array) != 0) { + zend_hash_internal_pointer_reset(array); + if (USED_RET()) { + php_array_iter_return_current(return_value, array, true); + } + } else { /* array->nInternalPointer is already 0 if the array is empty, even after removing elements */ - RETURN_FALSE; + RETVAL_FALSE; } - zend_hash_internal_pointer_reset(array); - - if (USED_RET()) { - php_array_iter_return_current(return_value, array, true); + if (iap_obj) { + OBJ_RELEASE(iap_obj); } } /* }}} */ @@ -1067,8 +1087,12 @@ PHP_FUNCTION(current) Z_PARAM_ARRAY_OR_OBJECT(array_zv) ZEND_PARSE_PARAMETERS_END(); - HashTable *array = get_ht_for_iap(array_zv, /* separate */ false); + zend_object *iap_obj; + HashTable *array = get_ht_for_iap(array_zv, /* separate */ false, &iap_obj); php_array_iter_return_current(return_value, array, true); + if (iap_obj) { + OBJ_RELEASE(iap_obj); + } } /* }}} */ @@ -1081,11 +1105,15 @@ PHP_FUNCTION(key) Z_PARAM_ARRAY_OR_OBJECT(array_zv) ZEND_PARSE_PARAMETERS_END(); - HashTable *array = get_ht_for_iap(array_zv, /* separate */ false); + zend_object *iap_obj; + HashTable *array = get_ht_for_iap(array_zv, /* separate */ false, &iap_obj); zval *entry = php_array_iter_seek_current(array, true); if (EXPECTED(entry)) { zend_hash_get_current_key_zval(array, return_value); } + if (iap_obj) { + OBJ_RELEASE(iap_obj); + } } /* }}} */ diff --git a/ext/standard/tests/array/gh20042.phpt b/ext/standard/tests/array/gh20042.phpt new file mode 100644 index 000000000000..34ca982b2ae5 --- /dev/null +++ b/ext/standard/tests/array/gh20042.phpt @@ -0,0 +1,18 @@ +--TEST-- +GH-20042 (SEGV in array.c when the error handler reassigns the array during prev()) +--FILE-- + +--EXPECT-- +OK survived diff --git a/ext/standard/tests/array/sort/array_multisort_variation2.phpt b/ext/standard/tests/array/sort/array_multisort_variation2.phpt index b491b87bfda8..b3bdba7a63df 100644 --- a/ext/standard/tests/array/sort/array_multisort_variation2.phpt +++ b/ext/standard/tests/array/sort/array_multisort_variation2.phpt @@ -107,7 +107,7 @@ foreach($inputs as $key =>$value) { }; ?> ---EXPECT-- +--EXPECTF-- *** Testing array_multisort() : usage variation *** --int 0-- diff --git a/ext/standard/tests/class_object/get_class_methods_variation_001.phpt b/ext/standard/tests/class_object/get_class_methods_variation_001.phpt index f7b86a2977ac..71392b9f4dc1 100644 --- a/ext/standard/tests/class_object/get_class_methods_variation_001.phpt +++ b/ext/standard/tests/class_object/get_class_methods_variation_001.phpt @@ -109,25 +109,25 @@ get_class_methods(): Argument #1 ($object_or_class) must be an object or a valid Arg value 0.5 get_class_methods(): Argument #1 ($object_or_class) must be an object or a valid class name, float given -Error: 2 - Array to string conversion Arg value Array -get_class_methods(): Argument #1 ($object_or_class) must be an object or a valid class name, array given Error: 2 - Array to string conversion +get_class_methods(): Argument #1 ($object_or_class) must be an object or a valid class name, array given Arg value Array -get_class_methods(): Argument #1 ($object_or_class) must be an object or a valid class name, array given Error: 2 - Array to string conversion +get_class_methods(): Argument #1 ($object_or_class) must be an object or a valid class name, array given Arg value Array -get_class_methods(): Argument #1 ($object_or_class) must be an object or a valid class name, array given Error: 2 - Array to string conversion +get_class_methods(): Argument #1 ($object_or_class) must be an object or a valid class name, array given Arg value Array -get_class_methods(): Argument #1 ($object_or_class) must be an object or a valid class name, array given Error: 2 - Array to string conversion +get_class_methods(): Argument #1 ($object_or_class) must be an object or a valid class name, array given Arg value Array +Error: 2 - Array to string conversion get_class_methods(): Argument #1 ($object_or_class) must be an object or a valid class name, array given Arg value @@ -169,4 +169,4 @@ get_class_methods(): Argument #1 ($object_or_class) must be an object or a valid Arg value get_class_methods(): Argument #1 ($object_or_class) must be an object or a valid class name, null given -Done +Done \ No newline at end of file diff --git a/ext/standard/tests/class_object/get_parent_class_variation_002.phpt b/ext/standard/tests/class_object/get_parent_class_variation_002.phpt index 8faeff9dd8dc..eb951cfbfdce 100644 --- a/ext/standard/tests/class_object/get_parent_class_variation_002.phpt +++ b/ext/standard/tests/class_object/get_parent_class_variation_002.phpt @@ -112,25 +112,25 @@ get_parent_class(): Argument #1 ($object_or_class) must be an object or a valid Arg value 0.5 get_parent_class(): Argument #1 ($object_or_class) must be an object or a valid class name, float given -Error: 2 - Array to string conversion Arg value Array -get_parent_class(): Argument #1 ($object_or_class) must be an object or a valid class name, array given Error: 2 - Array to string conversion +get_parent_class(): Argument #1 ($object_or_class) must be an object or a valid class name, array given Arg value Array -get_parent_class(): Argument #1 ($object_or_class) must be an object or a valid class name, array given Error: 2 - Array to string conversion +get_parent_class(): Argument #1 ($object_or_class) must be an object or a valid class name, array given Arg value Array -get_parent_class(): Argument #1 ($object_or_class) must be an object or a valid class name, array given Error: 2 - Array to string conversion +get_parent_class(): Argument #1 ($object_or_class) must be an object or a valid class name, array given Arg value Array -get_parent_class(): Argument #1 ($object_or_class) must be an object or a valid class name, array given Error: 2 - Array to string conversion +get_parent_class(): Argument #1 ($object_or_class) must be an object or a valid class name, array given Arg value Array +Error: 2 - Array to string conversion get_parent_class(): Argument #1 ($object_or_class) must be an object or a valid class name, array given Arg value @@ -173,4 +173,4 @@ get_parent_class(): Argument #1 ($object_or_class) must be an object or a valid Arg value get_parent_class(): Argument #1 ($object_or_class) must be an object or a valid class name, null given -Done +Done \ No newline at end of file diff --git a/ext/standard/tests/class_object/is_subclass_of_variation_001.phpt b/ext/standard/tests/class_object/is_subclass_of_variation_001.phpt index f03841dbac7d..7e74d8022d61 100644 --- a/ext/standard/tests/class_object/is_subclass_of_variation_001.phpt +++ b/ext/standard/tests/class_object/is_subclass_of_variation_001.phpt @@ -79,12 +79,12 @@ foreach($values as $value) { echo "Done"; ?> ---EXPECT-- +--EXPECTF-- *** Testing is_subclass_of() : usage variations *** -Error: 2 - Undefined variable $undefined_var -Error: 2 - Undefined variable $unset_var - -Arg value 0 +%A +%A +%A +%A bool(false) Arg value 1 @@ -110,25 +110,25 @@ bool(false) Arg value 0.5 bool(false) -Error: 2 - Array to string conversion Arg value Array -bool(false) Error: 2 - Array to string conversion +bool(false) Arg value Array -bool(false) Error: 2 - Array to string conversion +bool(false) Arg value Array -bool(false) Error: 2 - Array to string conversion +bool(false) Arg value Array -bool(false) Error: 2 - Array to string conversion +bool(false) Arg value Array +Error: 2 - Array to string conversion bool(false) Arg value diff --git a/ext/standard/tests/class_object/is_subclass_of_variation_004.phpt b/ext/standard/tests/class_object/is_subclass_of_variation_004.phpt index d045a4c1a915..fed9ef8c864d 100644 --- a/ext/standard/tests/class_object/is_subclass_of_variation_004.phpt +++ b/ext/standard/tests/class_object/is_subclass_of_variation_004.phpt @@ -79,12 +79,12 @@ foreach($values as $value) { echo "Done"; ?> ---EXPECT-- +--EXPECTF-- *** Testing is_subclass_of() : usage variations *** -Error: 2 - Undefined variable $undefined_var -Error: 2 - Undefined variable $unset_var - -Arg value 0 +%A +%A +%A +%A bool(false) Arg value 1 @@ -110,25 +110,25 @@ bool(false) Arg value 0.5 bool(false) -Error: 2 - Array to string conversion Arg value Array -bool(false) Error: 2 - Array to string conversion +bool(false) Arg value Array -bool(false) Error: 2 - Array to string conversion +bool(false) Arg value Array -bool(false) Error: 2 - Array to string conversion +bool(false) Arg value Array -bool(false) Error: 2 - Array to string conversion +bool(false) Arg value Array +Error: 2 - Array to string conversion bool(false) Arg value diff --git a/ext/standard/tests/class_object/method_exists_variation_001.phpt b/ext/standard/tests/class_object/method_exists_variation_001.phpt index d45d2652d11c..1761475ace66 100644 --- a/ext/standard/tests/class_object/method_exists_variation_001.phpt +++ b/ext/standard/tests/class_object/method_exists_variation_001.phpt @@ -83,10 +83,10 @@ echo "Done"; ?> --EXPECT-- *** Testing method_exists() : usage variations *** -Error: 2 - Undefined variable $undefined_var -Error: 2 - Undefined variable $unset_var Arg value 0 +Error: 2 - Undefined variable $undefined_var +Error: 2 - Undefined variable $unset_var method_exists(): Argument #1 ($object_or_class) must be of type object|string, int given Arg value 1 @@ -112,25 +112,25 @@ method_exists(): Argument #1 ($object_or_class) must be of type object|string, f Arg value 0.5 method_exists(): Argument #1 ($object_or_class) must be of type object|string, float given -Error: 2 - Array to string conversion Arg value Array -method_exists(): Argument #1 ($object_or_class) must be of type object|string, array given Error: 2 - Array to string conversion +method_exists(): Argument #1 ($object_or_class) must be of type object|string, array given Arg value Array -method_exists(): Argument #1 ($object_or_class) must be of type object|string, array given Error: 2 - Array to string conversion +method_exists(): Argument #1 ($object_or_class) must be of type object|string, array given Arg value Array -method_exists(): Argument #1 ($object_or_class) must be of type object|string, array given Error: 2 - Array to string conversion +method_exists(): Argument #1 ($object_or_class) must be of type object|string, array given Arg value Array -method_exists(): Argument #1 ($object_or_class) must be of type object|string, array given Error: 2 - Array to string conversion +method_exists(): Argument #1 ($object_or_class) must be of type object|string, array given Arg value Array +Error: 2 - Array to string conversion method_exists(): Argument #1 ($object_or_class) must be of type object|string, array given Arg value @@ -170,4 +170,4 @@ method_exists(): Argument #1 ($object_or_class) must be of type object|string, n Arg value method_exists(): Argument #1 ($object_or_class) must be of type object|string, null given -Done +Done \ No newline at end of file diff --git a/ext/standard/tests/image/getimagesize_variation2.phpt b/ext/standard/tests/image/getimagesize_variation2.phpt index 045d9141a6bd..1728c668fab0 100644 --- a/ext/standard/tests/image/getimagesize_variation2.phpt +++ b/ext/standard/tests/image/getimagesize_variation2.phpt @@ -72,12 +72,12 @@ foreach($values as $key => $value) { }; ?> ---EXPECT-- +--EXPECTF-- *** Testing getimagesize() : usage variations *** -Error: 2 - Undefined variable $undefined_var -Error: 2 - Undefined variable $unset_var - --- Arg value 0 -- +%A +%A +%A +%A string(28) "4a46494600010201006000600000" -- Arg value 1 -- diff --git a/ext/zend_test/tests/observer_error_05.phpt b/ext/zend_test/tests/observer_error_05.phpt index d672e6405ec8..378bb00589ab 100644 --- a/ext/zend_test/tests/observer_error_05.phpt +++ b/ext/zend_test/tests/observer_error_05.phpt @@ -30,15 +30,15 @@ echo 'You should not see this.'; - - <{closure:%s:%d}> - - + + + <{closure:%s:%d}> + + Deprecated: Passing E_USER_ERROR to trigger_error() is deprecated since 8.4, throw an exception or call exit with a string message instead in %s on line %d Fatal error: Foo error in %s on line %d - - - + + diff --git a/tests/lang/bug22592.phpt b/tests/lang/bug22592.phpt index 24b5f50fdf5a..2505b2ff7212 100644 --- a/tests/lang/bug22592.phpt +++ b/tests/lang/bug22592.phpt @@ -39,12 +39,12 @@ var_dump($result); string(5) "* *-*" string(7) "* *-* *" string(7) "*4*-* *" -[Only the first byte will be assigned to the string offset] string(7) "*4*s* *" +[Only the first byte will be assigned to the string offset] string(8) "*4*s* *0" string(8) "*-*-* *0" -[Only the first byte will be assigned to the string offset] string(8) "*-*s*s*0" +[Only the first byte will be assigned to the string offset] string(8) "4-4s4s*0" string(9) "4-4s4s505" string(9) "454s4s505" diff --git a/tests/lang/bug25547.phpt b/tests/lang/bug25547.phpt index 53b951fd4543..49bdb5e75115 100644 --- a/tests/lang/bug25547.phpt +++ b/tests/lang/bug25547.phpt @@ -22,9 +22,9 @@ print_r($output); echo "Done"; ?> --EXPECT-- -handler(Undefined array key "foo") Array ( [foo] => 1 ) +handler(Undefined array key "foo") Done diff --git a/tests/lang/bug25922.phpt b/tests/lang/bug25922.phpt index 4927bfea776c..41aa72ae35d6 100644 --- a/tests/lang/bug25922.phpt +++ b/tests/lang/bug25922.phpt @@ -19,6 +19,6 @@ function test() test(); ?> --EXPECT-- +Undefined index here: '' Undefined variable $data Trying to access array offset on null -Undefined index here: ''