From 2b383848a738eda02bc0f5bf116b021a6e42c24a 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. Closes GH-18761. --- NEWS | 1 + 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 +-- 6 files changed, 27 insertions(+), 8 deletions(-) create mode 100644 ext/mbstring/tests/mb_encode_numericentity_references.phpt diff --git a/NEWS b/NEWS index b577d8fd948..6c30ac60aa8 100644 --- a/NEWS +++ b/NEWS @@ -12,6 +12,7 @@ PHP NEWS released on bailout). (DanielEScherzer and ilutov) . Fixed GH-18695 (zend_ast_export() - float number is not preserved). (Oleg Efimov) + . Fix handling of references in zval_try_get_long(). (nielsdos) - Curl: . Fix memory leak when setting a list via curl_setopt fails. (nielsdos) diff --git a/Zend/zend_operators.c b/Zend/zend_operators.c index ae75e95a71c..252b8df1ea0 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 129e007107d..2bdde08bcac 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 e848f21615b..ca5a8b8ec72 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 00000000000..682be9b2cd1 --- /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 89631960d56..25a4de9386d 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) {