diff --git a/Zend/tests/in-de-crement/object_cannot_incdec.phpt b/Zend/tests/in-de-crement/object_cannot_incdec.phpt new file mode 100644 index 0000000000000..39a41d61dd3e5 --- /dev/null +++ b/Zend/tests/in-de-crement/object_cannot_incdec.phpt @@ -0,0 +1,45 @@ +--TEST-- +Cannot increment/decrement objects +--FILE-- +getMessage(), PHP_EOL; + var_dump($o); +} +try { + $o--; +} catch (\TypeError $e) { + echo $e->getMessage(), PHP_EOL; + var_dump($o); +} +try { + ++$o; +} catch (\TypeError $e) { + echo $e->getMessage(), PHP_EOL; + var_dump($o); +} +try { + --$o; +} catch (\TypeError $e) { + echo $e->getMessage(), PHP_EOL; + var_dump($o); +} +?> +--EXPECT-- +Cannot increment Foo +object(Foo)#1 (0) { +} +Cannot decrement Foo +object(Foo)#1 (0) { +} +Cannot increment Foo +object(Foo)#1 (0) { +} +Cannot decrement Foo +object(Foo)#1 (0) { +} diff --git a/Zend/tests/in-de-crement/object_cannot_incdec_use_result_op.phpt b/Zend/tests/in-de-crement/object_cannot_incdec_use_result_op.phpt new file mode 100644 index 0000000000000..b4c193a67182c --- /dev/null +++ b/Zend/tests/in-de-crement/object_cannot_incdec_use_result_op.phpt @@ -0,0 +1,45 @@ +--TEST-- +Cannot increment/decrement objects +--FILE-- +getMessage(), PHP_EOL; + var_dump($o); +} +try { + $y = $o--; +} catch (\TypeError $e) { + echo $e->getMessage(), PHP_EOL; + var_dump($o); +} +try { + $y = ++$o; +} catch (\TypeError $e) { + echo $e->getMessage(), PHP_EOL; + var_dump($o); +} +try { + $y = --$o; +} catch (\TypeError $e) { + echo $e->getMessage(), PHP_EOL; + var_dump($o); +} +?> +--EXPECT-- +Cannot increment Foo +object(Foo)#1 (0) { +} +Cannot decrement Foo +object(Foo)#1 (0) { +} +Cannot increment Foo +object(Foo)#1 (0) { +} +Cannot decrement Foo +object(Foo)#1 (0) { +} diff --git a/Zend/tests/in-de-crement/oss-fuzz-60709_globals.phpt b/Zend/tests/in-de-crement/oss-fuzz-60709_globals.phpt new file mode 100644 index 0000000000000..d6405a28f9669 --- /dev/null +++ b/Zend/tests/in-de-crement/oss-fuzz-60709_globals.phpt @@ -0,0 +1,36 @@ +--TEST-- +oss-fuzz #60709: Test +--FILE-- + +--EXPECT-- +POST DEC +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 +PRE DEC +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) diff --git a/Zend/tests/in-de-crement/oss-fuzz-60734_predec-object.phpt b/Zend/tests/in-de-crement/oss-fuzz-60734_predec-object.phpt new file mode 100644 index 0000000000000..0f0e19c8c2d53 --- /dev/null +++ b/Zend/tests/in-de-crement/oss-fuzz-60734_predec-object.phpt @@ -0,0 +1,14 @@ +--TEST-- +OSS Fuzz #60734: use-after-free visible in ASAN build pre decrement. +--FILE-- + +--EXPECTF-- +Fatal error: Uncaught TypeError: Cannot decrement Foo in %s:%d +Stack trace: +#0 {main} + thrown in %s on line %d diff --git a/Zend/tests/in-de-crement/oss-fuzz-60734_preinc-object.phpt b/Zend/tests/in-de-crement/oss-fuzz-60734_preinc-object.phpt new file mode 100644 index 0000000000000..3bae551f04f4c --- /dev/null +++ b/Zend/tests/in-de-crement/oss-fuzz-60734_preinc-object.phpt @@ -0,0 +1,14 @@ +--TEST-- +OSS Fuzz #60734: use-after-free visible in ASAN build pre increment. +--FILE-- + +--EXPECTF-- +Fatal error: Uncaught TypeError: Cannot increment Foo in %s:%d +Stack trace: +#0 {main} + thrown in %s on line %d 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 new file mode 100644 index 0000000000000..d2f27ed323249 --- /dev/null +++ b/Zend/tests/in-de-crement/unset_globals_in_error_handler.phpt @@ -0,0 +1,130 @@ +--TEST-- +Unset variable via $GLOBALS array in error_handler +--FILE-- + +--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 +NULL +Empty string +POST INC +Increment on non-alphanumeric string is deprecated +string(0) "" +POST DEC +Decrement on empty string is deprecated as non-numeric +string(0) "" +PRE INC +Increment on non-alphanumeric string is deprecated +string(1) "1" +PRE DEC +Decrement on empty string is deprecated as non-numeric +int(-1) +Non fill ASCII (only ++) +POST INC +Increment on non-alphanumeric string is deprecated +string(4) " ad " +PRE INC +Increment on non-alphanumeric string is deprecated +string(4) " ad " +Bool +POST INC +Increment on type bool has no effect, this will change in the next major version of PHP +bool(false) +POST DEC +Decrement on type bool has no effect, this will change in the next major version of PHP +bool(false) +PRE INC +Increment on type bool has no effect, this will change in the next major version of PHP +bool(false) +PRE DEC +Decrement on type bool has no effect, this will change in the next major version of PHP +bool(false) +POST INC +Increment on type bool has no effect, this will change in the next major version of PHP +bool(true) +POST DEC +Decrement on type bool has no effect, this will change in the next major version of PHP +bool(true) +PRE INC +Increment on type bool has no effect, this will change in the next major version of PHP +bool(true) +PRE DEC +Decrement on type bool has no effect, this will change in the next major version of PHP +bool(true) 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 new file mode 100644 index 0000000000000..33d18b9a62797 --- /dev/null +++ b/Zend/tests/in-de-crement/unset_object_property_in_error_handler.phpt @@ -0,0 +1,143 @@ +--TEST-- +Unset property via error_handler +--FILE-- +a); + } +} + +$c = new C; +set_error_handler([$c, 'errorHandler']); + +/* default property value */ +var_dump(--$c->a); + +echo "NULL (only --)\n"; +echo "POST DEC\n"; +$c->a = null; +var_dump($c->a--); +unset($c->a); +echo "PRE DEC\n"; +$c->a = null; +var_dump(--$c->a); +unset($c->a); +echo "Empty string\n"; +echo "POST INC\n"; +$c->a = ""; +var_dump($c->a++); +unset($c->a); +echo "POST DEC\n"; +$c->a = ""; +var_dump($c->a--); +unset($c->a); +echo "PRE INC\n"; +$c->a = ""; +var_dump(++$c->a); +unset($c->a); +echo "PRE DEC\n"; +$c->a = ""; +var_dump(--$c->a); +unset($c->a); +echo "Non fill ASCII (only ++)\n"; +echo "POST INC\n"; +$c->a = " ad "; +var_dump($c->a++); +unset($c->a); +echo "PRE INC\n"; +$c->a = " ad "; +var_dump(++$c->a); +unset($c->a); +echo "Bool\n"; +echo "POST INC\n"; +$c->a = false; +var_dump($c->a++); +unset($c->a); +echo "POST DEC\n"; +$c->a = false; +var_dump($c->a--); +unset($c->a); +echo "PRE INC\n"; +$c->a = false; +var_dump(++$c->a); +unset($c->a); +echo "PRE DEC\n"; +$c->a = false; +var_dump(--$c->a); +unset($c->a); +echo "POST INC\n"; +$c->a = true; +var_dump($c->a++); +unset($c->a); +echo "POST DEC\n"; +$c->a = true; +var_dump($c->a--); +unset($c->a); +echo "PRE INC\n"; +$c->a = true; +var_dump(++$c->a); +unset($c->a); +echo "PRE DEC\n"; +$c->a = true; +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 +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" +NULL +Empty string +POST INC +string(50) "Increment on non-alphanumeric string is deprecated" +string(0) "" +POST DEC +string(54) "Decrement on empty string is deprecated as non-numeric" +string(0) "" +PRE INC +string(50) "Increment on non-alphanumeric string is deprecated" +string(1) "1" +PRE DEC +string(54) "Decrement on empty string is deprecated as non-numeric" +int(-1) +Non fill ASCII (only ++) +POST INC +string(50) "Increment on non-alphanumeric string is deprecated" +string(4) " ad " +PRE INC +string(50) "Increment on non-alphanumeric string is deprecated" +string(4) " ad " +Bool +POST INC +string(87) "Increment on type bool has no effect, this will change in the next major version of PHP" +bool(false) +POST DEC +string(87) "Decrement on type bool has no effect, this will change in the next major version of PHP" +bool(false) +PRE INC +string(87) "Increment on type bool has no effect, this will change in the next major version of PHP" +bool(false) +PRE DEC +string(87) "Decrement on type bool has no effect, this will change in the next major version of PHP" +bool(false) +POST INC +string(87) "Increment on type bool has no effect, this will change in the next major version of PHP" +bool(true) +POST DEC +string(87) "Decrement on type bool has no effect, this will change in the next major version of PHP" +bool(true) +PRE INC +string(87) "Increment on type bool has no effect, this will change in the next major version of PHP" +bool(true) +PRE DEC +string(87) "Decrement on type bool has no effect, this will change in the next major version of PHP" +bool(true) diff --git a/Zend/zend_operators.c b/Zend/zend_operators.c index 293d7cc029eac..7f1b3234acea3 100644 --- a/Zend/zend_operators.c +++ b/Zend/zend_operators.c @@ -2657,9 +2657,18 @@ ZEND_API zend_result ZEND_FASTCALL increment_function(zval *op1) /* {{{ */ } break; case IS_FALSE: - case IS_TRUE: + case IS_TRUE: { + /* Error handler can undef/change type of op1, save it and reset it in case those cases */ + zval copy; + ZVAL_COPY_VALUE(©, op1); zend_error(E_WARNING, "Increment on type bool has no effect, this will change in the next major version of PHP"); + zval_ptr_dtor(op1); + ZVAL_COPY_VALUE(op1, ©); + if (EG(exception)) { + return FAILURE; + } break; + } case IS_REFERENCE: op1 = Z_REFVAL_P(op1); goto try_again; @@ -2735,19 +2744,31 @@ ZEND_API zend_result ZEND_FASTCALL decrement_function(zval *op1) /* {{{ */ } } break; - case IS_NULL: + case IS_NULL: { + /* Error handler can undef/change type of op1, save it and reset it in case those cases */ + zval copy; + ZVAL_COPY_VALUE(©, op1); zend_error(E_WARNING, "Decrement on type null has no effect, this will change in the next major version of PHP"); + zval_ptr_dtor(op1); + ZVAL_COPY_VALUE(op1, ©); if (EG(exception)) { return FAILURE; } break; + } case IS_FALSE: - case IS_TRUE: + case IS_TRUE: { + /* Error handler can undef/change type of op1, save it and reset it in case those cases */ + zval copy; + ZVAL_COPY_VALUE(©, op1); zend_error(E_WARNING, "Decrement on type bool has no effect, this will change in the next major version of PHP"); + zval_ptr_dtor(op1); + ZVAL_COPY_VALUE(op1, ©); if (EG(exception)) { return FAILURE; } break; + } case IS_REFERENCE: op1 = Z_REFVAL_P(op1); goto try_again; diff --git a/Zend/zend_vm_def.h b/Zend/zend_vm_def.h index b2e3e60a29417..1a95059020018 100644 --- a/Zend/zend_vm_def.h +++ b/Zend/zend_vm_def.h @@ -1501,6 +1501,10 @@ ZEND_VM_HELPER(zend_pre_inc_helper, VAR|CV, ANY) } increment_function(var_ptr); if (UNEXPECTED(EG(exception))) { + /* Smart branch expects result to be set with exceptions */ + if (UNEXPECTED(RETURN_VALUE_USED(opline))) { + ZVAL_NULL(EX_VAR(opline->result.var)); + } HANDLE_EXCEPTION(); } } while (0); @@ -1556,6 +1560,10 @@ ZEND_VM_HELPER(zend_pre_dec_helper, VAR|CV, ANY) } decrement_function(var_ptr); if (UNEXPECTED(EG(exception))) { + /* Smart branch expects result to be set with exceptions */ + if (UNEXPECTED(RETURN_VALUE_USED(opline))) { + ZVAL_NULL(EX_VAR(opline->result.var)); + } HANDLE_EXCEPTION(); } } while (0); diff --git a/Zend/zend_vm_execute.h b/Zend/zend_vm_execute.h index 1db9b06a17884..881dec2d9d3fd 100644 --- a/Zend/zend_vm_execute.h +++ b/Zend/zend_vm_execute.h @@ -21625,6 +21625,10 @@ static zend_never_inline ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_pre_inc_help } increment_function(var_ptr); if (UNEXPECTED(EG(exception))) { + /* Smart branch expects result to be set with exceptions */ + if (UNEXPECTED(RETURN_VALUE_USED(opline))) { + ZVAL_NULL(EX_VAR(opline->result.var)); + } HANDLE_EXCEPTION(); } } while (0); @@ -21698,6 +21702,10 @@ static zend_never_inline ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_pre_dec_help } decrement_function(var_ptr); if (UNEXPECTED(EG(exception))) { + /* Smart branch expects result to be set with exceptions */ + if (UNEXPECTED(RETURN_VALUE_USED(opline))) { + ZVAL_NULL(EX_VAR(opline->result.var)); + } HANDLE_EXCEPTION(); } } while (0); @@ -39000,6 +39008,10 @@ static zend_never_inline ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_pre_inc_help } increment_function(var_ptr); if (UNEXPECTED(EG(exception))) { + /* Smart branch expects result to be set with exceptions */ + if (UNEXPECTED(RETURN_VALUE_USED(opline))) { + ZVAL_NULL(EX_VAR(opline->result.var)); + } HANDLE_EXCEPTION(); } } while (0); @@ -39072,6 +39084,10 @@ static zend_never_inline ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_pre_dec_help } decrement_function(var_ptr); if (UNEXPECTED(EG(exception))) { + /* Smart branch expects result to be set with exceptions */ + if (UNEXPECTED(RETURN_VALUE_USED(opline))) { + ZVAL_NULL(EX_VAR(opline->result.var)); + } HANDLE_EXCEPTION(); } } while (0);