From f38b306e903933e7ef58cdb13683a54028baa6d3 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Wed, 4 Jun 2025 19:52:21 +0200 Subject: [PATCH] Fix handling of references in zval_try_get_long() This API can't handle references, yet everyone keeps forgetting that it can't and that you should DEREF upfront. Fix every type of this issue once and for all by moving the reference handling to this Zend API. --- Zend/zend_operators.c | 9 +++++++++ ext/intl/dateformat/dateformat_parse.c | 8 +++----- ext/mbstring/mbstring.c | 2 +- .../tests/mb_encode_numericentity_references.phpt | 12 ++++++++++++ ext/pcntl/pcntl.c | 3 +-- 5 files changed, 26 insertions(+), 8 deletions(-) create mode 100644 ext/mbstring/tests/mb_encode_numericentity_references.phpt diff --git a/Zend/zend_operators.c b/Zend/zend_operators.c index ae75e95a71c1d..252b8df1ea0fb 100644 --- a/Zend/zend_operators.c +++ b/Zend/zend_operators.c @@ -378,6 +378,7 @@ static zend_always_inline zend_result zendi_try_convert_scalar_to_number(zval *o static zend_never_inline zend_long ZEND_FASTCALL zendi_try_get_long(const zval *op, bool *failed) /* {{{ */ { *failed = 0; +try_again: switch (Z_TYPE_P(op)) { case IS_NULL: case IS_FALSE: @@ -448,6 +449,14 @@ static zend_never_inline zend_long ZEND_FASTCALL zendi_try_get_long(const zval * case IS_ARRAY: *failed = 1; return 0; + case IS_REFERENCE: + op = Z_REFVAL_P(op); + if (Z_TYPE_P(op) == IS_LONG) { + return Z_LVAL_P(op); + } else { + goto try_again; + } + break; EMPTY_SWITCH_DEFAULT_CASE() } } diff --git a/ext/intl/dateformat/dateformat_parse.c b/ext/intl/dateformat/dateformat_parse.c index 129e007107db7..2bdde08bcaced 100644 --- a/ext/intl/dateformat/dateformat_parse.c +++ b/ext/intl/dateformat/dateformat_parse.c @@ -185,12 +185,10 @@ PHP_METHOD(IntlDateFormatter, parseToCalendar) DATE_FORMAT_METHOD_FETCH_OBJECT; if (z_parse_pos) { - zval *z_parse_pos_tmp = z_parse_pos; - ZVAL_DEREF(z_parse_pos_tmp); - bool failed = false; - zend_long long_parse_pos = zval_try_get_long(z_parse_pos_tmp, &failed); + bool failed; + zend_long long_parse_pos = zval_try_get_long(z_parse_pos, &failed); if (failed) { - zend_argument_type_error(2, "must be of type int, %s given", zend_zval_value_name(z_parse_pos_tmp)); + zend_argument_type_error(2, "must be of type int, %s given", zend_zval_value_name(z_parse_pos)); RETURN_THROWS(); } if (ZEND_LONG_INT_OVFL(long_parse_pos)) { diff --git a/ext/mbstring/mbstring.c b/ext/mbstring/mbstring.c index e848f21615bca..ca5a8b8ec72e4 100644 --- a/ext/mbstring/mbstring.c +++ b/ext/mbstring/mbstring.c @@ -3930,7 +3930,7 @@ static uint32_t *make_conversion_map(HashTable *target_hash, size_t *conversion_ uint32_t *mapelm = convmap; ZEND_HASH_FOREACH_VAL(target_hash, hash_entry) { - bool failed = true; + bool failed; zend_long tmp = zval_try_get_long(hash_entry, &failed); if (failed) { efree(convmap); diff --git a/ext/mbstring/tests/mb_encode_numericentity_references.phpt b/ext/mbstring/tests/mb_encode_numericentity_references.phpt new file mode 100644 index 0000000000000..682be9b2cd1e2 --- /dev/null +++ b/ext/mbstring/tests/mb_encode_numericentity_references.phpt @@ -0,0 +1,12 @@ +--TEST-- +mb_encode_numericentity() reference handling +--EXTENSIONS-- +mbstring +--FILE-- + +--EXPECT-- +string(0) "" diff --git a/ext/pcntl/pcntl.c b/ext/pcntl/pcntl.c index 89631960d5677..25a4de9386d44 100644 --- a/ext/pcntl/pcntl.c +++ b/ext/pcntl/pcntl.c @@ -874,8 +874,7 @@ static bool php_pcntl_set_user_signal_infos( zval *user_signal_no; ZEND_HASH_FOREACH_VAL(user_signals, user_signal_no) { - bool failed = true; - ZVAL_DEREF(user_signal_no); + bool failed; zend_long tmp = zval_try_get_long(user_signal_no, &failed); if (failed) {