From 62ecf54f35e227d09d19ec40920f7bb8d29e9356 Mon Sep 17 00:00:00 2001 From: Nikita Popov Date: Wed, 7 Jul 2021 09:36:30 +0200 Subject: [PATCH] Fix use after free on compound division by zero We can't destroy the result operand early, because the division might fail, in which case we need to preserve the original value. Place the division result in a temporary zval, and only copy it on success. Fixes oss-fuzz #35876. --- Zend/tests/div_by_zero_compound_refcounted.phpt | 16 ++++++++++++++++ Zend/zend_operators.c | 12 ++++++------ 2 files changed, 22 insertions(+), 6 deletions(-) create mode 100644 Zend/tests/div_by_zero_compound_refcounted.phpt diff --git a/Zend/tests/div_by_zero_compound_refcounted.phpt b/Zend/tests/div_by_zero_compound_refcounted.phpt new file mode 100644 index 00000000000..7f0f59622b4 --- /dev/null +++ b/Zend/tests/div_by_zero_compound_refcounted.phpt @@ -0,0 +1,16 @@ +--TEST-- +Division by zero in compound assignment with refcounted operand +--FILE-- +getMessage(), "\n"; +} +var_dump($h); +?> +--EXPECT-- +Division by zero +string(2) "12" diff --git a/Zend/zend_operators.c b/Zend/zend_operators.c index 193aade5fb3..b4fe753a070 100644 --- a/Zend/zend_operators.c +++ b/Zend/zend_operators.c @@ -1314,7 +1314,7 @@ ZEND_API zend_result ZEND_FASTCALL div_function(zval *result, zval *op1, zval *o ZEND_TRY_BINARY_OBJECT_OPERATION(ZEND_DIV); - zval op1_copy, op2_copy; + zval result_copy, op1_copy, op2_copy; if (UNEXPECTED(zendi_try_convert_scalar_to_number(op1, &op1_copy) == FAILURE) || UNEXPECTED(zendi_try_convert_scalar_to_number(op2, &op2_copy) == FAILURE)) { zend_binop_error("/", op1, op2); @@ -1324,12 +1324,12 @@ ZEND_API zend_result ZEND_FASTCALL div_function(zval *result, zval *op1, zval *o return FAILURE; } - if (result == op1) { - zval_ptr_dtor(result); - } - - retval = div_function_base(result, &op1_copy, &op2_copy); + retval = div_function_base(&result_copy, &op1_copy, &op2_copy); if (retval == SUCCESS) { + if (result == op1) { + zval_ptr_dtor(result); + } + ZVAL_COPY_VALUE(result, &result_copy); return SUCCESS; }