From c889faab35a9ece483552114896cb80c15949888 Mon Sep 17 00:00:00 2001 From: David Carlier Date: Fri, 29 May 2026 07:00:54 +0100 Subject: [PATCH 1/2] ext/zip: memory leak when zip cancel callback bails out. Fix #22176 A cancel callback that throws during the implicit zip_close() in the shutdown destructor triggers a zend_bailout that longjmps through libzip, skipping its free(filelist). Wrap the call in zend_try/zend_catch and cancel on bailout so libzip can unwind and clean up. While at it, apply the same guard to the progress callback, which is invoked from libzip the same way and is prone to the identical leak. close GH-22177 --- NEWS | 3 +++ ext/zip/php_zip.c | 37 ++++++++++++++++++++++++----- ext/zip/php_zip.h | 1 + ext/zip/tests/gh22176.phpt | 33 +++++++++++++++++++++++++ ext/zip/tests/gh22176_progress.phpt | 33 +++++++++++++++++++++++++ 5 files changed, 101 insertions(+), 6 deletions(-) create mode 100644 ext/zip/tests/gh22176.phpt create mode 100644 ext/zip/tests/gh22176_progress.phpt diff --git a/NEWS b/NEWS index 370095987ea7..e476e2b83036 100644 --- a/NEWS +++ b/NEWS @@ -55,6 +55,9 @@ PHP NEWS - Zip: . Fixed bug GH-21705 (ZipArchive::getFromIndex() ignores ZipArchive::FL_UNCHANGED for deleted entries). (Weilin Du) + . Fixed bug GH-22176 (memory leak with ZipArchive::registerCancelBack() + is used with reference returning function during shutdown). + (David Carlier) 02 Jul 2026, PHP 8.6.0alpha1 diff --git a/ext/zip/php_zip.c b/ext/zip/php_zip.c index 5df4d47740a8..9bc801455ed6 100644 --- a/ext/zip/php_zip.c +++ b/ext/zip/php_zip.c @@ -628,6 +628,12 @@ static bool php_zipobj_close(ze_zip_object *obj, zend_string **out_str) /* {{{ * obj->za = NULL; obj->from_string = false; + + if (obj->bailout_callback) { + obj->bailout_callback = false; + zend_bailout(); + } + return success; } /* }}} */ @@ -1073,10 +1079,16 @@ static void php_zip_object_dtor(zend_object *object) if (intern->za) { if (zip_close(intern->za) != 0) { - php_error_docref(NULL, E_WARNING, "Cannot destroy the zip context: %s", zip_strerror(intern->za)); + if (!intern->bailout_callback) { + php_error_docref(NULL, E_WARNING, "Cannot destroy the zip context: %s", zip_strerror(intern->za)); + } zip_discard(intern->za); } intern->za = NULL; + if (intern->bailout_callback) { + intern->bailout_callback = false; + zend_bailout(); + } } } @@ -2985,15 +2997,21 @@ PHP_METHOD(ZipArchive, getStream) #ifdef HAVE_PROGRESS_CALLBACK static void php_zip_progress_callback(zip_t *arch, double state, void *ptr) { - if (!EG(active)) { + ze_zip_object *obj = ptr; + + if (UNEXPECTED(!EG(active) || obj->bailout_callback)) { return; } zval cb_args[1]; - ze_zip_object *obj = ptr; ZVAL_DOUBLE(&cb_args[0], state); - zend_call_known_fcc(&obj->progress_callback, NULL, 1, cb_args, NULL); + + zend_try { + zend_call_known_fcc(&obj->progress_callback, NULL, 1, cb_args, NULL); + } zend_catch { + obj->bailout_callback = true; + } zend_end_try(); } /* {{{ register a progression callback: void callback(double state); */ @@ -3036,11 +3054,18 @@ static int php_zip_cancel_callback(zip_t *arch, void *ptr) zval cb_retval; ze_zip_object *obj = ptr; - if (!EG(active)) { + if (UNEXPECTED(!EG(active) || obj->bailout_callback)) { return 0; } - zend_call_known_fcc(&obj->cancel_callback, &cb_retval, 0, NULL, NULL); + zend_try { + zend_call_known_fcc(&obj->cancel_callback, &cb_retval, 0, NULL, NULL); + } zend_catch { + obj->bailout_callback = true; + /* Cancel if a bailout occurs to allow cleanup to happen */ + return -1; + } zend_end_try(); + if (Z_ISUNDEF(cb_retval)) { /* Cancel if an exception has been thrown */ return -1; diff --git a/ext/zip/php_zip.h b/ext/zip/php_zip.h index 9e57a54de1cc..408c29708231 100644 --- a/ext/zip/php_zip.h +++ b/ext/zip/php_zip.h @@ -74,6 +74,7 @@ typedef struct _ze_zip_object { zip_int64_t last_id; int err_zip; int err_sys; + bool bailout_callback; #ifdef HAVE_PROGRESS_CALLBACK zend_fcall_info_cache progress_callback; #endif diff --git a/ext/zip/tests/gh22176.phpt b/ext/zip/tests/gh22176.phpt new file mode 100644 index 000000000000..90b6327072c9 --- /dev/null +++ b/ext/zip/tests/gh22176.phpt @@ -0,0 +1,33 @@ +--TEST-- +GH-22176 (Memory leak when a ZipArchive cancel callback bails out in the shutdown destructor) +--EXTENSIONS-- +zip +--SKIPIF-- + +--FILE-- +open(__DIR__ . '/gh22176_cancel.zip', ZipArchive::CREATE); +$zip->registerCancelCallback(function () { + throw new \Exception('cancel boom'); +}); +$zip->addFromString('test', 'test'); +echo "done\n"; +// The archive is flushed and the object destroyed during request shutdown; +// the thrown exception bails out through libzip's zip_close() without leaking +// its internal state, and the bailout resumes once libzip has unwound. +?> +--CLEAN-- + +--EXPECTF-- +done + +Fatal error: Uncaught Exception: cancel boom in %s:%d +Stack trace: +#0 [internal function]: {closure:%s:%d}() +#1 {main} + thrown in %s on line %d diff --git a/ext/zip/tests/gh22176_progress.phpt b/ext/zip/tests/gh22176_progress.phpt new file mode 100644 index 000000000000..5507fe24ab98 --- /dev/null +++ b/ext/zip/tests/gh22176_progress.phpt @@ -0,0 +1,33 @@ +--TEST-- +GH-22176 (Memory leak when a ZipArchive progress callback bails out in the shutdown destructor) +--EXTENSIONS-- +zip +--SKIPIF-- + +--FILE-- +open(__DIR__ . '/gh22176_progress.zip', ZipArchive::CREATE); +$zip->registerProgressCallback(0.5, function ($r) { + throw new \Exception('progress boom'); +}); +$zip->addFromString('test', 'test'); +echo "done\n"; +// The archive is flushed and the object destroyed during request shutdown; +// the thrown exception bails out through libzip's zip_close() without leaking +// its internal state, and the bailout resumes once libzip has unwound. +?> +--CLEAN-- + +--EXPECTF-- +done + +Fatal error: Uncaught Exception: progress boom in %s:%d +Stack trace: +#0 [internal function]: {closure:%s:%d}(0.0) +#1 {main} + thrown in %s on line %d From 8613e0ebdb8ac759029e3350580f5c6468e750d0 Mon Sep 17 00:00:00 2001 From: Weilin Du Date: Sun, 5 Jul 2026 20:10:00 +0800 Subject: [PATCH 2/2] ext/Intl: Add `UNEXPECTED()` in obvious error path (#22601) This added `UNEXPECTED()` in several obvious error path for optimization in ext/intl. --- .../codepointiterator_internal.cpp | 10 +++---- .../rulebasedbreakiterator_methods.cpp | 8 +++--- ext/intl/common/common_date.cpp | 2 +- ext/intl/converter/converter.cpp | 6 ++--- ext/intl/dateformat/dateformat_format.cpp | 4 +-- ext/intl/formatter/formatter_attr.cpp | 4 +-- ext/intl/formatter/formatter_parse.cpp | 4 +-- ext/intl/grapheme/grapheme_string.cpp | 26 +++++++++---------- ext/intl/grapheme/grapheme_util.cpp | 2 +- ext/intl/idn/idn.cpp | 10 +++---- ext/intl/intl_convert.c | 4 +-- ext/intl/intl_data.h | 4 +-- .../listformatter/listformatter_class.cpp | 2 +- ext/intl/locale/locale_methods.cpp | 16 ++++++------ ext/intl/normalizer/normalizer_normalize.cpp | 16 ++++++------ ext/intl/php_intl.c | 2 +- .../rangeformatter/rangeformatter_class.cpp | 2 +- .../resourcebundle/resourcebundle_class.cpp | 4 +-- ext/intl/spoofchecker/spoofchecker_main.cpp | 4 +-- .../transliterator/transliterator_methods.cpp | 8 +++--- ext/intl/uchar/uchar.cpp | 6 ++--- 21 files changed, 72 insertions(+), 72 deletions(-) diff --git a/ext/intl/breakiterator/codepointiterator_internal.cpp b/ext/intl/breakiterator/codepointiterator_internal.cpp index 47d5df00c1be..0604cfa24167 100644 --- a/ext/intl/breakiterator/codepointiterator_internal.cpp +++ b/ext/intl/breakiterator/codepointiterator_internal.cpp @@ -126,7 +126,7 @@ void CodePointBreakIterator::setText(const UnicodeString &text) void CodePointBreakIterator::setText(UText *text, UErrorCode &status) { - if (U_FAILURE(status)) { + if (UNEXPECTED(U_FAILURE(status))) { return; } @@ -234,7 +234,7 @@ CodePointBreakIterator *CodePointBreakIterator::createBufferClone( void *stackBuffer, int32_t &bufferSize, UErrorCode &status) { //see implementation of RuleBasedBreakIterator::createBufferClone() - if (U_FAILURE(status)) { + if (UNEXPECTED(U_FAILURE(status))) { return NULL; } @@ -273,17 +273,17 @@ CodePointBreakIterator *CodePointBreakIterator::createBufferClone( CodePointBreakIterator &CodePointBreakIterator::refreshInputText(UText *input, UErrorCode &status) { //see implementation of RuleBasedBreakIterator::createBufferClone() - if (U_FAILURE(status)) { + if (UNEXPECTED(U_FAILURE(status))) { return *this; } - if (input == NULL) { + if (UNEXPECTED(input == NULL)) { status = U_ILLEGAL_ARGUMENT_ERROR; return *this; } int64_t pos = utext_getNativeIndex(this->fText); this->fText = utext_clone(this->fText, input, false, true, &status); - if (U_FAILURE(status)) { + if (UNEXPECTED(U_FAILURE(status))) { return *this; } diff --git a/ext/intl/breakiterator/rulebasedbreakiterator_methods.cpp b/ext/intl/breakiterator/rulebasedbreakiterator_methods.cpp index 1e1c2f0f6e5c..4526b9faab13 100644 --- a/ext/intl/breakiterator/rulebasedbreakiterator_methods.cpp +++ b/ext/intl/breakiterator/rulebasedbreakiterator_methods.cpp @@ -57,7 +57,7 @@ U_CFUNC PHP_METHOD(IntlRuleBasedBreakIterator, __construct) if (!compiled) { UnicodeString rulesStr; UParseError parseError = UParseError(); - if (intl_stringFromChar(rulesStr, ZSTR_VAL(rules), ZSTR_LEN(rules), &status) == FAILURE) { + if (UNEXPECTED(intl_stringFromChar(rulesStr, ZSTR_VAL(rules), ZSTR_LEN(rules), &status) == FAILURE)) { zend_throw_exception(IntlException_ce_ptr, "IntlRuleBasedBreakIterator::__construct(): rules were not a valid UTF-8 string", 0); RETURN_THROWS(); @@ -65,7 +65,7 @@ U_CFUNC PHP_METHOD(IntlRuleBasedBreakIterator, __construct) rbbi = new RuleBasedBreakIterator(rulesStr, parseError, status); intl_error_set_code(NULL, status); - if (U_FAILURE(status)) { + if (UNEXPECTED(U_FAILURE(status))) { smart_str parse_error_str; parse_error_str = intl_parse_error_to_string(&parseError); zend_throw_exception_ex(IntlException_ce_ptr, 0, @@ -78,7 +78,7 @@ U_CFUNC PHP_METHOD(IntlRuleBasedBreakIterator, __construct) } } else { // compiled rbbi = new RuleBasedBreakIterator(reinterpret_cast(ZSTR_VAL(rules)), ZSTR_LEN(rules), status); - if (U_FAILURE(status)) { + if (UNEXPECTED(U_FAILURE(status))) { zend_throw_exception(IntlException_ce_ptr, "IntlRuleBasedBreakIterator::__construct(): unable to create instance from compiled rules", 0); delete rbbi; @@ -165,7 +165,7 @@ U_CFUNC PHP_METHOD(IntlRuleBasedBreakIterator, getBinaryRules) uint32_t rules_len; const uint8_t *rules = fetch_rbbi(bio)->getBinaryRules(rules_len); - if (rules_len > INT_MAX - 1) { + if (UNEXPECTED(rules_len > INT_MAX - 1)) { intl_errors_set(BREAKITER_ERROR_P(bio), BREAKITER_ERROR_CODE(bio), "the rules are too large"); RETURN_FALSE; diff --git a/ext/intl/common/common_date.cpp b/ext/intl/common/common_date.cpp index fb5ca6edaebe..ed18bba70b9f 100644 --- a/ext/intl/common/common_date.cpp +++ b/ext/intl/common/common_date.cpp @@ -55,7 +55,7 @@ U_CFUNC TimeZone *timezone_convert_datetimezone( minutes = offset_mins - hours * 60; minutes *= minutes > 0 ? 1 : -1; - if (offset_mins <= -24 * 60 || offset_mins >= 24 * 60) { + if (UNEXPECTED(offset_mins <= -24 * 60 || offset_mins >= 24 * 60)) { intl_errors_set(outside_error, U_ILLEGAL_ARGUMENT_ERROR, "object has an time zone offset that's too large"); return NULL; diff --git a/ext/intl/converter/converter.cpp b/ext/intl/converter/converter.cpp index b4d62d4643d1..402a50482485 100644 --- a/ext/intl/converter/converter.cpp +++ b/ext/intl/converter/converter.cpp @@ -162,7 +162,7 @@ static void php_converter_append_toUnicode_target(zval *val, UConverterToUnicode case IS_LONG: { const zend_long lval = Z_LVAL_P(val); - if ((lval < 0) || (lval > 0x10FFFF)) { + if (UNEXPECTED((lval < 0) || (lval > 0x10FFFF))) { php_converter_throw_failure(objval, U_ILLEGAL_ARGUMENT_ERROR, "Invalid codepoint U+%04lx", lval); return; } @@ -636,7 +636,7 @@ static zend_string* php_converter_do_convert(UConverter *dest_cnv, zend_string *ret; UChar *temp; - if (!src_cnv || !dest_cnv) { + if (UNEXPECTED(!src_cnv || !dest_cnv)) { php_converter_throw_failure(objval, U_INVALID_STATE_ERROR, "Internal converters not initialized"); return nullptr; @@ -686,7 +686,7 @@ static zend_string* php_converter_do_convert(UConverter *dest_cnv, static void php_converter_set_subst_chars(UConverter *cnv, const zend_string *subst, UErrorCode *error) { - if (ZSTR_LEN(subst) > SCHAR_MAX) { + if (UNEXPECTED(ZSTR_LEN(subst) > SCHAR_MAX)) { *error = U_ILLEGAL_ARGUMENT_ERROR; return; } diff --git a/ext/intl/dateformat/dateformat_format.cpp b/ext/intl/dateformat/dateformat_format.cpp index 8b2a28d5c4d2..8890332dd406 100644 --- a/ext/intl/dateformat/dateformat_format.cpp +++ b/ext/intl/dateformat/dateformat_format.cpp @@ -73,8 +73,8 @@ static int32_t internal_get_arr_ele(IntlDateFormatter_object *dfo, intl_errors_set(err, U_ILLEGAL_ARGUMENT_ERROR, message); efree(message); } else { - if (Z_LVAL_P(ele_value) > INT32_MAX || - Z_LVAL_P(ele_value) < INT32_MIN) { + if (UNEXPECTED(Z_LVAL_P(ele_value) > INT32_MAX || + Z_LVAL_P(ele_value) < INT32_MIN)) { spprintf(&message, 0, "value " ZEND_LONG_FMT " is out of " "bounds for a 32-bit integer in key '%s'", Z_LVAL_P(ele_value), key_name); diff --git a/ext/intl/formatter/formatter_attr.cpp b/ext/intl/formatter/formatter_attr.cpp index 4e97ad7f7105..ff215fdffbd7 100644 --- a/ext/intl/formatter/formatter_attr.cpp +++ b/ext/intl/formatter/formatter_attr.cpp @@ -234,7 +234,7 @@ U_CFUNC PHP_FUNCTION( numfmt_get_symbol ) UNumberFormatSymbol symbol = static_cast(lsymbol); - if(symbol >= UNUM_FORMAT_SYMBOL_COUNT || symbol < 0) { + if (UNEXPECTED(symbol >= UNUM_FORMAT_SYMBOL_COUNT || symbol < 0)) { intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR, "invalid symbol value"); RETURN_FALSE; } @@ -276,7 +276,7 @@ U_CFUNC PHP_FUNCTION( numfmt_set_symbol ) UNumberFormatSymbol symbol = static_cast(lsymbol); - if (symbol >= UNUM_FORMAT_SYMBOL_COUNT || symbol < 0) { + if (UNEXPECTED(symbol >= UNUM_FORMAT_SYMBOL_COUNT || symbol < 0)) { intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR, "invalid symbol value"); RETURN_FALSE; } diff --git a/ext/intl/formatter/formatter_parse.cpp b/ext/intl/formatter/formatter_parse.cpp index 63c0fba4329f..40107229e0ae 100644 --- a/ext/intl/formatter/formatter_parse.cpp +++ b/ext/intl/formatter/formatter_parse.cpp @@ -51,7 +51,7 @@ U_CFUNC PHP_FUNCTION( numfmt_parse ) if (zposition) { zend_long long_position = zval_get_long(zposition); - if (long_position < INT32_MIN || long_position > INT32_MAX) { + if (UNEXPECTED(long_position < INT32_MIN || long_position > INT32_MAX)) { zend_argument_value_error(hasThis() ? 3 : 4, "must be between %d and %d", INT32_MIN, INT32_MAX); RETURN_THROWS(); } @@ -162,7 +162,7 @@ U_CFUNC PHP_FUNCTION( numfmt_parse_currency ) if (zposition) { zend_long long_position = zval_get_long(zposition); - if (long_position < INT32_MIN || long_position > INT32_MAX) { + if (UNEXPECTED(long_position < INT32_MIN || long_position > INT32_MAX)) { zend_argument_value_error(hasThis() ? 3 : 4, "must be between %d and %d", INT32_MIN, INT32_MAX); RETURN_THROWS(); } diff --git a/ext/intl/grapheme/grapheme_string.cpp b/ext/intl/grapheme/grapheme_string.cpp index eb8b7e609b8e..5e614be6ae72 100644 --- a/ext/intl/grapheme/grapheme_string.cpp +++ b/ext/intl/grapheme/grapheme_string.cpp @@ -104,7 +104,7 @@ U_CFUNC PHP_FUNCTION(grapheme_strpos) Z_PARAM_PATH(locale, locale_len) ZEND_PARSE_PARAMETERS_END(); - if ( OUTSIDE_STRING(loffset, haystack_len) ) { + if (UNEXPECTED(OUTSIDE_STRING(loffset, haystack_len))) { zend_argument_value_error(3, "must be contained in argument #1 ($haystack)"); RETURN_THROWS(); } @@ -158,7 +158,7 @@ U_CFUNC PHP_FUNCTION(grapheme_stripos) Z_PARAM_PATH(locale, locale_len) ZEND_PARSE_PARAMETERS_END(); - if ( OUTSIDE_STRING(loffset, haystack_len) ) { + if (UNEXPECTED(OUTSIDE_STRING(loffset, haystack_len))) { zend_argument_value_error(3, "must be contained in argument #1 ($haystack)"); RETURN_THROWS(); } @@ -224,7 +224,7 @@ U_CFUNC PHP_FUNCTION(grapheme_strrpos) Z_PARAM_PATH(locale, locale_len) ZEND_PARSE_PARAMETERS_END(); - if ( OUTSIDE_STRING(loffset, haystack_len) ) { + if (UNEXPECTED(OUTSIDE_STRING(loffset, haystack_len))) { zend_argument_value_error(3, "must be contained in argument #1 ($haystack)"); RETURN_THROWS(); } @@ -283,7 +283,7 @@ U_CFUNC PHP_FUNCTION(grapheme_strripos) Z_PARAM_PATH(locale, locale_len) ZEND_PARSE_PARAMETERS_END(); - if ( OUTSIDE_STRING(loffset, haystack_len) ) { + if (UNEXPECTED(OUTSIDE_STRING(loffset, haystack_len))) { zend_argument_value_error(3, "must be contained in argument #1 ($haystack)"); RETURN_THROWS(); } @@ -358,7 +358,7 @@ U_CFUNC PHP_FUNCTION(grapheme_substr) Z_PARAM_PATH(locale, locale_len) ZEND_PARSE_PARAMETERS_END(); - if (lstart < INT32_MIN || lstart > INT32_MAX) { + if (UNEXPECTED(lstart < INT32_MIN || lstart > INT32_MAX)) { zend_argument_value_error(2, "is too large"); RETURN_THROWS(); } @@ -369,7 +369,7 @@ U_CFUNC PHP_FUNCTION(grapheme_substr) length = str_len; } - if (length < INT32_MIN || length > INT32_MAX) { + if (UNEXPECTED(length < INT32_MIN || length > INT32_MAX)) { zend_argument_value_error(3, "is too large"); RETURN_THROWS(); } @@ -758,17 +758,17 @@ U_CFUNC PHP_FUNCTION(grapheme_extract) RETURN_THROWS(); } - if ( lstart > INT32_MAX || lstart < 0 || (size_t)lstart >= str_len ) { + if (UNEXPECTED(lstart > INT32_MAX || lstart < 0 || (size_t)lstart >= str_len)) { intl_error_set( nullptr, U_ILLEGAL_ARGUMENT_ERROR, "start not contained in string"); RETURN_FALSE; } - if (size < 0) { + if (UNEXPECTED(size < 0)) { zend_argument_value_error(2, "must be greater than or equal to 0"); RETURN_THROWS(); } - if (size > INT32_MAX) { + if (UNEXPECTED(size > INT32_MAX)) { zend_argument_value_error(2, "is too large"); RETURN_THROWS(); } @@ -864,7 +864,7 @@ U_CFUNC PHP_FUNCTION(grapheme_str_split) Z_PARAM_LONG(split_len) ZEND_PARSE_PARAMETERS_END(); - if (split_len <= 0 || split_len > UINT_MAX / 4) { + if (UNEXPECTED(split_len <= 0 || split_len > UINT_MAX / 4)) { zend_argument_value_error(2, "must be greater than 0 and less than or equal to %d", UINT_MAX / 4); RETURN_THROWS(); } @@ -943,17 +943,17 @@ U_CFUNC PHP_FUNCTION(grapheme_levenshtein) Z_PARAM_PATH(locale, locale_len) ZEND_PARSE_PARAMETERS_END(); - if (cost_ins <= 0 || cost_ins > UINT_MAX / 4) { + if (UNEXPECTED(cost_ins <= 0 || cost_ins > UINT_MAX / 4)) { zend_argument_value_error(3, "must be greater than 0 and less than or equal to %d", UINT_MAX / 4); RETURN_THROWS(); } - if (cost_rep <= 0 || cost_rep > UINT_MAX / 4) { + if (UNEXPECTED(cost_rep <= 0 || cost_rep > UINT_MAX / 4)) { zend_argument_value_error(4, "must be greater than 0 and less than or equal to %d", UINT_MAX / 4); RETURN_THROWS(); } - if (cost_del <= 0 || cost_del > UINT_MAX / 4) { + if (UNEXPECTED(cost_del <= 0 || cost_del > UINT_MAX / 4)) { zend_argument_value_error(5, "must be greater than 0 and less than or equal to %d", UINT_MAX / 4); RETURN_THROWS(); } diff --git a/ext/intl/grapheme/grapheme_util.cpp b/ext/intl/grapheme/grapheme_util.cpp index 1e6fb94c9408..031f1c717891 100644 --- a/ext/intl/grapheme/grapheme_util.cpp +++ b/ext/intl/grapheme/grapheme_util.cpp @@ -59,7 +59,7 @@ U_CFUNC void grapheme_substr_ascii(char *str, size_t str_len, int32_t f, int32_t int32_t str_len2 = (int32_t)str_len; /* in order to avoid signed/unsigned problems */ *sub_str = NULL; - if(str_len > INT32_MAX) { + if (UNEXPECTED(str_len > INT32_MAX)) { /* We cannot return long strings from ICU functions, so we won't here too */ return; } diff --git a/ext/intl/idn/idn.cpp b/ext/intl/idn/idn.cpp index 28708eb93508..76cca2281296 100644 --- a/ext/intl/idn/idn.cpp +++ b/ext/intl/idn/idn.cpp @@ -40,7 +40,7 @@ enum { static zend_result php_intl_idn_check_status(UErrorCode err, const char *msg) { intl_error_set_code(NULL, err); - if (U_FAILURE(err)) { + if (UNEXPECTED(U_FAILURE(err))) { intl_error_set_custom_msg(NULL, msg); return FAILURE; } @@ -59,7 +59,7 @@ static void php_intl_idn_to_46(INTERNAL_FUNCTION_PARAMETERS, UIDNAInfo info = UIDNA_INFO_INITIALIZER; uts46 = uidna_openUTS46(option, &status); - if (php_intl_idn_check_status(status, "failed to open UIDNA instance") == FAILURE) { + if (UNEXPECTED(php_intl_idn_check_status(status, "failed to open UIDNA instance") == FAILURE)) { RETURN_FALSE; } @@ -74,7 +74,7 @@ static void php_intl_idn_to_46(INTERNAL_FUNCTION_PARAMETERS, len = uidna_nameToUnicodeUTF8(uts46, ZSTR_VAL(domain), ZSTR_LEN(domain), ZSTR_VAL(buffer), buffer_capac, &info, &status); } - if (len >= buffer_capac || php_intl_idn_check_status(status, "failed to convert name") == FAILURE) { + if (UNEXPECTED(len >= buffer_capac || php_intl_idn_check_status(status, "failed to convert name") == FAILURE)) { uidna_close(uts46); zend_string_efree(buffer); RETURN_FALSE; @@ -121,7 +121,7 @@ static void php_intl_idn_handoff(INTERNAL_FUNCTION_PARAMETERS, int mode) zend_argument_must_not_be_empty_error(1); RETURN_THROWS(); } - if (ZSTR_LEN(domain) > INT32_MAX - 1) { + if (UNEXPECTED(ZSTR_LEN(domain) > INT32_MAX - 1)) { zend_argument_value_error(1, "must be less than " PRId32 " bytes", INT32_MAX); RETURN_THROWS(); } @@ -133,7 +133,7 @@ static void php_intl_idn_handoff(INTERNAL_FUNCTION_PARAMETERS, int mode) if (idna_info != NULL) { idna_info = zend_try_array_init(idna_info); - if (!idna_info) { + if (UNEXPECTED(!idna_info)) { RETURN_THROWS(); } } diff --git a/ext/intl/intl_convert.c b/ext/intl/intl_convert.c index abbcc6c1fa73..5497e84508b4 100644 --- a/ext/intl/intl_convert.c +++ b/ext/intl/intl_convert.c @@ -59,7 +59,7 @@ void intl_convert_utf8_to_utf16( */ *status = U_ZERO_ERROR; - if(src_len > INT32_MAX) { + if (UNEXPECTED(src_len > INT32_MAX)) { /* we cannot fit this string */ *status = U_BUFFER_OVERFLOW_ERROR; return; @@ -120,7 +120,7 @@ zend_string *intl_convert_utf8_to_utf16_zstr( *status = U_ZERO_ERROR; - if(src_len > INT32_MAX) { + if (UNEXPECTED(src_len > INT32_MAX)) { /* we cannot fit this string */ *status = U_BUFFER_OVERFLOW_ERROR; return NULL; diff --git a/ext/intl/intl_data.h b/ext/intl/intl_data.h index c36f0adf5665..d2021caf18e3 100644 --- a/ext/intl/intl_data.h +++ b/ext/intl/intl_data.h @@ -114,7 +114,7 @@ typedef struct _intl_data { #define INTL_MAX_LOCALE_LEN (ULOC_FULLNAME_CAPACITY-1) #define INTL_CHECK_LOCALE_LEN(locale_len) \ - if((locale_len) > INTL_MAX_LOCALE_LEN) { \ + if (UNEXPECTED((locale_len) > INTL_MAX_LOCALE_LEN)) { \ char *_msg; \ spprintf(&_msg, 0, "Locale string too long, should be no longer than %d characters", INTL_MAX_LOCALE_LEN); \ intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR, _msg); \ @@ -123,7 +123,7 @@ typedef struct _intl_data { } #define INTL_CHECK_LOCALE_LEN_OR_FAILURE(locale_len) \ - if((locale_len) > INTL_MAX_LOCALE_LEN) { \ + if (UNEXPECTED((locale_len) > INTL_MAX_LOCALE_LEN)) { \ char *_msg; \ spprintf(&_msg, 0, "Locale string too long, should be no longer than %d characters", INTL_MAX_LOCALE_LEN); \ intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR, _msg); \ diff --git a/ext/intl/listformatter/listformatter_class.cpp b/ext/intl/listformatter/listformatter_class.cpp index 0f432b0e6056..59c01259227d 100644 --- a/ext/intl/listformatter/listformatter_class.cpp +++ b/ext/intl/listformatter/listformatter_class.cpp @@ -83,7 +83,7 @@ PHP_METHOD(IntlListFormatter, __construct) locale = (char *)intl_locale_get_default(); } - if (locale_len > INTL_MAX_LOCALE_LEN) { + if (UNEXPECTED(locale_len > INTL_MAX_LOCALE_LEN)) { zend_argument_value_error(1, "must be less than or equal to %d characters", INTL_MAX_LOCALE_LEN); RETURN_THROWS(); } diff --git a/ext/intl/locale/locale_methods.cpp b/ext/intl/locale/locale_methods.cpp index 0733ffc8abb7..05ec5825d8cb 100644 --- a/ext/intl/locale/locale_methods.cpp +++ b/ext/intl/locale/locale_methods.cpp @@ -353,7 +353,7 @@ static zend_string* get_icu_value_internal( const char* loc_name , const char* t int32_t buflen = 512; UErrorCode status = U_ZERO_ERROR; - if (strlen(loc_name) > INTL_MAX_LOCALE_LEN) { + if (UNEXPECTED(strlen(loc_name) > INTL_MAX_LOCALE_LEN)) { return NULL; } @@ -575,7 +575,7 @@ static void get_icu_disp_value_src_php( const char* tag_name, INTERNAL_FUNCTION_ Z_PARAM_PATH_OR_NULL(disp_loc_name, disp_loc_name_len) ZEND_PARSE_PARAMETERS_END(); - if(loc_name_len > ULOC_FULLNAME_CAPACITY) { + if (UNEXPECTED(loc_name_len > ULOC_FULLNAME_CAPACITY)) { /* See bug 67397: overlong locale names cause trouble in uloc_getDisplayName */ intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR, "name too long"); RETURN_FALSE; @@ -710,7 +710,7 @@ static void get_icu_disp_keyword_value_src_php(const char* tag_name, INTERNAL_FU Z_PARAM_PATH_OR_NULL(disp_loc_name, disp_loc_name_len) ZEND_PARSE_PARAMETERS_END(); - if (loc_name_len > ULOC_FULLNAME_CAPACITY) { + if (UNEXPECTED(loc_name_len > ULOC_FULLNAME_CAPACITY)) { intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR, "name too long"); RETURN_FALSE; } @@ -1715,7 +1715,7 @@ U_CFUNC PHP_FUNCTION(locale_accept_from_http) ZEND_PARSE_PARAMETERS_START(1, 1) Z_PARAM_STRING(http_accept, http_accept_len) ZEND_PARSE_PARAMETERS_END(); - if(http_accept_len > ULOC_FULLNAME_CAPACITY) { + if (UNEXPECTED(http_accept_len > ULOC_FULLNAME_CAPACITY)) { /* check each fragment, if any bigger than capacity, can't do it due to bug #72533 */ char *start = http_accept; char *end; @@ -1723,7 +1723,7 @@ U_CFUNC PHP_FUNCTION(locale_accept_from_http) do { end = strchr(start, ','); len = end ? end-start : http_accept_len-(start-http_accept); - if(len > ULOC_FULLNAME_CAPACITY) { + if (UNEXPECTED(len > ULOC_FULLNAME_CAPACITY)) { intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR, "locale string too long"); RETURN_FALSE; @@ -1740,7 +1740,7 @@ U_CFUNC PHP_FUNCTION(locale_accept_from_http) &outResult, http_accept, available, &status); uenum_close(available); INTL_CHECK_STATUS(status, "failed to find acceptable locale"); - if (len < 0 || outResult == ULOC_ACCEPT_FAILED) { + if (UNEXPECTED(len < 0 || outResult == ULOC_ACCEPT_FAILED)) { RETURN_FALSE; } RETURN_STRINGL(resultLocale, len); @@ -1779,7 +1779,7 @@ U_CFUNC PHP_FUNCTION(locale_add_likely_subtags) const int32_t maximized_locale_len = uloc_addLikelySubtags(locale, maximized_locale, sizeof(maximized_locale), &status); INTL_CHECK_STATUS(status, "invalid locale"); - if (maximized_locale_len < 0) { + if (UNEXPECTED(maximized_locale_len < 0)) { RETURN_FALSE; } @@ -1802,7 +1802,7 @@ U_CFUNC PHP_FUNCTION(locale_minimize_subtags) const int32_t minimized_locale_len = uloc_minimizeSubtags(locale, minimized_locale, sizeof(minimized_locale), &status); INTL_CHECK_STATUS(status, "invalid locale"); - if (minimized_locale_len < 0) { + if (UNEXPECTED(minimized_locale_len < 0)) { RETURN_FALSE; } diff --git a/ext/intl/normalizer/normalizer_normalize.cpp b/ext/intl/normalizer/normalizer_normalize.cpp index 846eda64b4c8..8a77d37ad4d4 100644 --- a/ext/intl/normalizer/normalizer_normalize.cpp +++ b/ext/intl/normalizer/normalizer_normalize.cpp @@ -54,7 +54,7 @@ static const UNormalizer2 *intl_get_normalizer(zend_long form, UErrorCode *err) static int32_t intl_normalize(zend_long form, const UChar *src, int32_t src_len, UChar *dst, int32_t dst_len, UErrorCode *err) {/*{{{*/ const UNormalizer2 *norm = intl_get_normalizer(form, err); - if (U_FAILURE(*err)) { + if (UNEXPECTED(U_FAILURE(*err))) { return -1; } @@ -65,7 +65,7 @@ static UBool intl_is_normalized(zend_long form, const UChar *uinput, int32_t uin {/*{{{*/ const UNormalizer2 *norm = intl_get_normalizer(form, err); - if(U_FAILURE(*err)) { + if (UNEXPECTED(U_FAILURE(*err))) { return false; } @@ -151,7 +151,7 @@ U_CFUNC PHP_FUNCTION( normalizer_normalize ) * (U_BUFFER_OVERFLOW_ERROR means that *target buffer is not large enough). * (U_STRING_NOT_TERMINATED_WARNING usually means that the input string is empty). */ - if( U_FAILURE(status) && status != U_BUFFER_OVERFLOW_ERROR && status != U_STRING_NOT_TERMINATED_WARNING ) { + if (UNEXPECTED(U_FAILURE(status) && status != U_BUFFER_OVERFLOW_ERROR && status != U_STRING_NOT_TERMINATED_WARNING)) { intl_error_set_custom_msg( NULL, "Error normalizing string"); efree( uret_buf ); efree( uinput ); @@ -172,7 +172,7 @@ U_CFUNC PHP_FUNCTION( normalizer_normalize ) size_needed = intl_normalize(form, uinput, uinput_len, uret_buf, uret_len, &status); /* Bail out if an unexpected error occurred. */ - if( U_FAILURE(status) ) { + if (UNEXPECTED(U_FAILURE(status))) { /* Set error messages. */ intl_error_set_custom_msg( NULL,"Error normalizing string"); efree( uret_buf ); @@ -264,7 +264,7 @@ U_CFUNC PHP_FUNCTION( normalizer_is_normalized ) efree( uinput ); /* Bail out if an unexpected error occurred. */ - if( U_FAILURE(status) ) { + if (UNEXPECTED(U_FAILURE(status))) { /* Set error messages. */ intl_error_set_custom_msg( NULL,"Error testing if string is the given normalization form."); RETURN_FALSE; @@ -304,20 +304,20 @@ U_CFUNC PHP_FUNCTION( normalizer_get_raw_decomposition ) norm = intl_get_normalizer(form, &status); U8_NEXT(input, offset, input_length, codepoint); - if ((size_t)offset != input_length) { + if (UNEXPECTED((size_t)offset != input_length)) { intl_error_set_code(NULL, U_ILLEGAL_ARGUMENT_ERROR); intl_error_set_custom_msg(NULL, "Input string must be exactly one UTF-8 encoded code point long."); return; } - if ((codepoint < UCHAR_MIN_VALUE) || (codepoint > UCHAR_MAX_VALUE)) { + if (UNEXPECTED((codepoint < UCHAR_MIN_VALUE) || (codepoint > UCHAR_MAX_VALUE))) { intl_error_set_code(NULL, U_ILLEGAL_ARGUMENT_ERROR); intl_error_set_custom_msg(NULL, "Code point out of range"); return; } decomposition_length = unorm2_getRawDecomposition(norm, codepoint, decomposition, 32, &status); - if (decomposition_length == -1) { + if (UNEXPECTED(decomposition_length == -1)) { RETURN_NULL(); } diff --git a/ext/intl/php_intl.c b/ext/intl/php_intl.c index 9882309c6f46..697d77a98bfa 100644 --- a/ext/intl/php_intl.c +++ b/ext/intl/php_intl.c @@ -106,7 +106,7 @@ char* canonicalize_locale_string(const char* locale) { canonicalized_len = uloc_canonicalize(locale, canonicalized, sizeof(canonicalized), &status); - if (U_FAILURE(status) || canonicalized_len <= 0) { + if (UNEXPECTED(U_FAILURE(status) || canonicalized_len <= 0)) { return NULL; } diff --git a/ext/intl/rangeformatter/rangeformatter_class.cpp b/ext/intl/rangeformatter/rangeformatter_class.cpp index f933e0c06090..2dbb60c5b639 100644 --- a/ext/intl/rangeformatter/rangeformatter_class.cpp +++ b/ext/intl/rangeformatter/rangeformatter_class.cpp @@ -99,7 +99,7 @@ U_CFUNC PHP_METHOD(IntlNumberRangeFormatter, createFromSkeleton) locale = (char *)intl_locale_get_default(); } - if (locale_len > INTL_MAX_LOCALE_LEN) { + if (UNEXPECTED(locale_len > INTL_MAX_LOCALE_LEN)) { zend_argument_value_error(2, "must be no longer than %d characters", INTL_MAX_LOCALE_LEN); RETURN_THROWS(); } diff --git a/ext/intl/resourcebundle/resourcebundle_class.cpp b/ext/intl/resourcebundle/resourcebundle_class.cpp index 7e22e9e8c7df..a997691f6423 100644 --- a/ext/intl/resourcebundle/resourcebundle_class.cpp +++ b/ext/intl/resourcebundle/resourcebundle_class.cpp @@ -108,7 +108,7 @@ static zend_result resourcebundle_ctor(INTERNAL_FUNCTION_PARAMETERS) locale = (char *)intl_locale_get_default(); } - if (bundlename_len >= MAXPATHLEN) { + if (UNEXPECTED(bundlename_len >= MAXPATHLEN)) { zend_argument_value_error(2, "is too long"); return FAILURE; } @@ -349,7 +349,7 @@ U_CFUNC PHP_FUNCTION( resourcebundle_locales ) Z_PARAM_STRING(bundlename, bundlename_len) ZEND_PARSE_PARAMETERS_END(); - if (bundlename_len >= MAXPATHLEN) { + if (UNEXPECTED(bundlename_len >= MAXPATHLEN)) { zend_argument_value_error(1, "is too long"); RETURN_THROWS(); } diff --git a/ext/intl/spoofchecker/spoofchecker_main.cpp b/ext/intl/spoofchecker/spoofchecker_main.cpp index cfd5ad8fb631..1e8e9ac2a78d 100644 --- a/ext/intl/spoofchecker/spoofchecker_main.cpp +++ b/ext/intl/spoofchecker/spoofchecker_main.cpp @@ -77,7 +77,7 @@ U_CFUNC PHP_METHOD(Spoofchecker, areConfusable) ZEND_PARSE_PARAMETERS_END(); SPOOFCHECKER_METHOD_FETCH_OBJECT; - if(ZSTR_LEN(s1) > INT32_MAX || ZSTR_LEN(s2) > INT32_MAX) { + if (UNEXPECTED(ZSTR_LEN(s1) > INT32_MAX || ZSTR_LEN(s2) > INT32_MAX)) { SPOOFCHECKER_ERROR_CODE(co) = U_BUFFER_OVERFLOW_ERROR; } else { ret = uspoof_areConfusableUTF8(co->uspoof, ZSTR_VAL(s1), (int32_t)ZSTR_LEN(s1), ZSTR_VAL(s2), (int32_t)ZSTR_LEN(s2), SPOOFCHECKER_ERROR_CODE_P(co)); @@ -179,7 +179,7 @@ U_CFUNC PHP_METHOD(Spoofchecker, setAllowedChars) ZEND_PARSE_PARAMETERS_END(); SPOOFCHECKER_METHOD_FETCH_OBJECT; - if (ZSTR_LEN(pattern) > INT32_MAX) { + if (UNEXPECTED(ZSTR_LEN(pattern) > INT32_MAX)) { zend_argument_value_error(1, "must be less than or equal to " ZEND_LONG_FMT " bytes long", INT32_MAX); RETURN_THROWS(); } diff --git a/ext/intl/transliterator/transliterator_methods.cpp b/ext/intl/transliterator/transliterator_methods.cpp index 8efcff95b310..9cdec2611a24 100644 --- a/ext/intl/transliterator/transliterator_methods.cpp +++ b/ext/intl/transliterator/transliterator_methods.cpp @@ -331,17 +331,17 @@ U_CFUNC PHP_FUNCTION( transliterator_transliterate ) RETURN_THROWS(); } - if (limit < -1) { + if (UNEXPECTED(limit < -1)) { zend_argument_value_error(is_method ? 3 : 4, "must be greater than or equal to -1"); goto cleanup_object; } - if (start < 0) { + if (UNEXPECTED(start < 0)) { zend_argument_value_error(is_method ? 2 : 3, "must be greater than or equal to 0"); goto cleanup_object; } - if (limit != -1 && start > limit) { + if (UNEXPECTED(limit != -1 && start > limit)) { zend_argument_value_error(is_method ? 2 : 3, "must be less than or equal to argument #%d ($end)", is_method ? 3 : 4); goto cleanup_object; } @@ -355,7 +355,7 @@ U_CFUNC PHP_FUNCTION( transliterator_transliterate ) /* we've started allocating resources, goto from now on */ - if( ( start > ustr_len ) || (( limit != -1 ) && (limit > ustr_len ) ) ) + if (UNEXPECTED((start > ustr_len) || ((limit != -1) && (limit > ustr_len)))) { char *msg; spprintf( &msg, 0, diff --git a/ext/intl/uchar/uchar.cpp b/ext/intl/uchar/uchar.cpp index 8dc6a58a8287..80bdd2bdb9fa 100644 --- a/ext/intl/uchar/uchar.cpp +++ b/ext/intl/uchar/uchar.cpp @@ -30,21 +30,21 @@ static inline int convert_cp(UChar32* pcp, const zend_string *string_codepoint, int32_t i = 0; const size_t string_codepoint_length = ZSTR_LEN(string_codepoint); - if (ZEND_SIZE_T_INT_OVFL(string_codepoint_length)) { + if (UNEXPECTED(ZEND_SIZE_T_INT_OVFL(string_codepoint_length))) { intl_error_set_code(NULL, U_ILLEGAL_ARGUMENT_ERROR); intl_error_set_custom_msg(NULL, "Input string is too long."); return FAILURE; } U8_NEXT(ZSTR_VAL(string_codepoint), i, string_codepoint_length, int_codepoint); - if ((size_t)i != string_codepoint_length) { + if (UNEXPECTED((size_t)i != string_codepoint_length)) { intl_error_set_code(NULL, U_ILLEGAL_ARGUMENT_ERROR); intl_error_set_custom_msg(NULL, "Passing a UTF-8 character for codepoint requires a string which is exactly one UTF-8 codepoint long."); return FAILURE; } } - if ((int_codepoint < UCHAR_MIN_VALUE) || (int_codepoint > UCHAR_MAX_VALUE)) { + if (UNEXPECTED((int_codepoint < UCHAR_MIN_VALUE) || (int_codepoint > UCHAR_MAX_VALUE))) { intl_error_set_code(NULL, U_ILLEGAL_ARGUMENT_ERROR); intl_error_set_custom_msg(NULL, "Codepoint out of range"); return FAILURE;