diff --git a/NEWS b/NEWS index bef0ac90460a0..a3d278ac2e3b3 100644 --- a/NEWS +++ b/NEWS @@ -88,6 +88,7 @@ PHP NEWS (BogdanUngureanu) . Added Locale::isRightToLeft to check if a locale is written right to left. (David Carlier) + . Added null bytes presence in locale inputs for Locale class. (David Carlier) - MySQLi: . Fixed bugs GH-17900 and GH-8084 (calling mysqli::__construct twice). diff --git a/UPGRADING b/UPGRADING index 1b2779c8c1e7c..40645a28ac600 100644 --- a/UPGRADING +++ b/UPGRADING @@ -212,6 +212,8 @@ PHP 8.5 UPGRADE NOTES . grapheme_extract() properly assigns $next value when skipping over invalid starting bytes. Previously there were cases where it would point to the start of the grapheme boundary instead of the end. + . Locale:: methods throw a ValueError when locale inputs contain null + bytes. - PCNTL: . pcntl_exec() now has a formal return type of false. diff --git a/ext/intl/locale/locale_methods.c b/ext/intl/locale/locale_methods.c index bba52a90994cc..684f84c7e323a 100644 --- a/ext/intl/locale/locale_methods.c +++ b/ext/intl/locale/locale_methods.c @@ -315,7 +315,7 @@ PHP_NAMED_FUNCTION(zif_locale_set_default) char *default_locale = NULL; ZEND_PARSE_PARAMETERS_START(1, 1) - Z_PARAM_STR(locale_name) + Z_PARAM_PATH_STR(locale_name) ZEND_PARSE_PARAMETERS_END(); if (ZSTR_LEN(locale_name) == 0) { @@ -481,7 +481,7 @@ static void get_icu_value_src_php( char* tag_name, INTERNAL_FUNCTION_PARAMETERS) intl_error_reset( NULL ); ZEND_PARSE_PARAMETERS_START(1, 1) - Z_PARAM_STRING(loc_name, loc_name_len) + Z_PARAM_PATH(loc_name, loc_name_len) ZEND_PARSE_PARAMETERS_END(); if(loc_name_len == 0) { @@ -568,9 +568,9 @@ static void get_icu_disp_value_src_php( char* tag_name, INTERNAL_FUNCTION_PARAME intl_error_reset( NULL ); ZEND_PARSE_PARAMETERS_START(1, 2) - Z_PARAM_STRING(loc_name, loc_name_len) + Z_PARAM_PATH(loc_name, loc_name_len) Z_PARAM_OPTIONAL - Z_PARAM_STRING_OR_NULL(disp_loc_name, disp_loc_name_len) + Z_PARAM_PATH_OR_NULL(disp_loc_name, disp_loc_name_len) ZEND_PARSE_PARAMETERS_END(); if(loc_name_len > ULOC_FULLNAME_CAPACITY) { @@ -735,7 +735,7 @@ PHP_FUNCTION( locale_get_keywords ) intl_error_reset( NULL ); ZEND_PARSE_PARAMETERS_START(1, 1) - Z_PARAM_STRING(loc_name, loc_name_len) + Z_PARAM_PATH(loc_name, loc_name_len) ZEND_PARSE_PARAMETERS_END(); INTL_CHECK_LOCALE_LEN(strlen(loc_name)); @@ -1126,7 +1126,7 @@ PHP_FUNCTION(locale_parse) intl_error_reset( NULL ); ZEND_PARSE_PARAMETERS_START(1, 1) - Z_PARAM_STRING(loc_name, loc_name_len) + Z_PARAM_PATH(loc_name, loc_name_len) ZEND_PARSE_PARAMETERS_END(); INTL_CHECK_LOCALE_LEN(strlen(loc_name)); @@ -1166,7 +1166,7 @@ PHP_FUNCTION(locale_get_all_variants) intl_error_reset( NULL ); ZEND_PARSE_PARAMETERS_START(1, 1) - Z_PARAM_STRING(loc_name, loc_name_len) + Z_PARAM_PATH(loc_name, loc_name_len) ZEND_PARSE_PARAMETERS_END(); if(loc_name_len == 0) { @@ -1260,8 +1260,8 @@ PHP_FUNCTION(locale_filter_matches) intl_error_reset( NULL ); ZEND_PARSE_PARAMETERS_START(2, 3) - Z_PARAM_STRING(lang_tag, lang_tag_len) - Z_PARAM_STRING(loc_range, loc_range_len) + Z_PARAM_PATH(lang_tag, lang_tag_len) + Z_PARAM_PATH(loc_range, loc_range_len) Z_PARAM_OPTIONAL Z_PARAM_BOOL(boolCanonical) ZEND_PARSE_PARAMETERS_END(); @@ -1434,6 +1434,10 @@ static zend_string* lookup_loc_range(const char* loc_range, HashTable* hash_arr, zend_argument_type_error(2, "must only contain string values"); LOOKUP_CLEAN_RETURN(NULL); } + if (zend_str_has_nul_byte(Z_STR_P(ele_value))) { + zend_argument_value_error(2, "must not contain any null bytes"); + LOOKUP_CLEAN_RETURN(NULL); + } cur_arr[cur_arr_len*2] = estrndup(Z_STRVAL_P(ele_value), Z_STRLEN_P(ele_value)); result = strToMatch(Z_STRVAL_P(ele_value), cur_arr[cur_arr_len*2]); if(result == 0) { @@ -1535,10 +1539,10 @@ PHP_FUNCTION(locale_lookup) ZEND_PARSE_PARAMETERS_START(2, 4) Z_PARAM_ARRAY(arr) - Z_PARAM_STRING(loc_range, loc_range_len) + Z_PARAM_PATH(loc_range, loc_range_len) Z_PARAM_OPTIONAL Z_PARAM_BOOL(boolCanonical) - Z_PARAM_STR_OR_NULL(fallback_loc_str) + Z_PARAM_PATH_STR_OR_NULL(fallback_loc_str) ZEND_PARSE_PARAMETERS_END(); if(loc_range_len == 0) { @@ -1626,7 +1630,7 @@ PHP_FUNCTION(locale_is_right_to_left) size_t locale_len; ZEND_PARSE_PARAMETERS_START(1, 1) - Z_PARAM_STRING(locale, locale_len) + Z_PARAM_PATH(locale, locale_len) ZEND_PARSE_PARAMETERS_END(); if (!locale_len) { diff --git a/ext/intl/tests/locale_filter_matches_icu70.phpt b/ext/intl/tests/locale_filter_matches_icu70.phpt index ce30b0565472f..0e0b16af17606 100644 --- a/ext/intl/tests/locale_filter_matches_icu70.phpt +++ b/ext/intl/tests/locale_filter_matches_icu70.phpt @@ -69,6 +69,18 @@ function ut_main() } } + try { + ut_loc_locale_filter_matches("de\0-DE", "de-DE", false); + } catch (\ValueError $e) { + echo $e->getMessage(). PHP_EOL; + } + + try { + ut_loc_locale_filter_matches("de-DE", "d\0e-DE", false); + } catch (\ValueError $e) { + echo $e->getMessage(). PHP_EOL; + } + $res_str .= "\n"; return $res_str; @@ -79,6 +91,10 @@ ut_run(); ?> --EXPECT-- +Locale::filterMatches(): Argument #1 ($languageTag) must not contain any null bytes +Locale::filterMatches(): Argument #2 ($locale) must not contain any null bytes +locale_filter_matches(): Argument #1 ($languageTag) must not contain any null bytes +locale_filter_matches(): Argument #2 ($locale) must not contain any null bytes -------------- loc_range:de-de matches lang_tag de-DEVA ? NO loc_range:de_DE canonically matches lang_tag de_Deva ? NO diff --git a/ext/intl/tests/locale_get_all_variants.phpt b/ext/intl/tests/locale_get_all_variants.phpt index 5b876d87eda95..12bf82cacac09 100644 --- a/ext/intl/tests/locale_get_all_variants.phpt +++ b/ext/intl/tests/locale_get_all_variants.phpt @@ -39,6 +39,12 @@ function ut_main() $res_str .= "\n"; } + try { + ut_loc_locale_get_all_variants("i-\0tay"); + } catch (\ValueError $e) { + echo $e->getMessage(). PHP_EOL; + } + $res_str .= "\n"; return $res_str; @@ -49,6 +55,8 @@ ut_run(); ?> --EXPECT-- +Locale::getAllVariants(): Argument #1 ($locale) must not contain any null bytes +locale_get_all_variants(): Argument #1 ($locale) must not contain any null bytes sl_IT_nedis_KIRTI : variants 'NEDIS','KIRTI', sl_IT_nedis-a-kirti-x-xyz : variants 'NEDIS', sl_IT_rozaj : variants 'ROZAJ', diff --git a/ext/intl/tests/locale_get_display_language4.phpt b/ext/intl/tests/locale_get_display_language4.phpt new file mode 100644 index 0000000000000..1273c4a99968d --- /dev/null +++ b/ext/intl/tests/locale_get_display_language4.phpt @@ -0,0 +1,29 @@ +--TEST-- +locale_get_display_language() throwing null bytes exceptions. +--EXTENSIONS-- +intl +--FILE-- +getMessage(). PHP_EOL; + } + + try { + ut_loc_get_display_language("a-DE", "locale=a\0-DE"); + } catch (\ValueError $e) { + echo $e->getMessage(). PHP_EOL; + } +} +include_once 'ut_common.inc'; +ut_run(); +?> +--EXPECT-- +Locale::getDisplayLanguage(): Argument #1 ($locale) must not contain any null bytes +Locale::getDisplayLanguage(): Argument #2 ($displayLocale) must not contain any null bytes +locale_get_display_language(): Argument #1 ($locale) must not contain any null bytes +locale_get_display_language(): Argument #2 ($displayLocale) must not contain any null bytes diff --git a/ext/intl/tests/locale_get_display_script4.phpt b/ext/intl/tests/locale_get_display_script4.phpt index 77c630393c88a..5330529330269 100644 --- a/ext/intl/tests/locale_get_display_script4.phpt +++ b/ext/intl/tests/locale_get_display_script4.phpt @@ -83,6 +83,18 @@ function ut_main() $res_str .= "-----------------\n"; } + try { + ut_loc_get_display_script("a-D\0E", "locale=a-DE"); + } catch (\ValueError $e) { + echo $e->getMessage(). PHP_EOL; + } + + try { + ut_loc_get_display_script("a-DE", "locale=a\0-DE"); + } catch (\ValueError $e) { + echo $e->getMessage(). PHP_EOL; + } + return $res_str; } @@ -92,6 +104,10 @@ ut_run(); ?> --EXPECT-- +Locale::getDisplayScript(): Argument #1 ($locale) must not contain any null bytes +Locale::getDisplayScript(): Argument #2 ($displayLocale) must not contain any null bytes +locale_get_display_script(): Argument #1 ($locale) must not contain any null bytes +locale_get_display_script(): Argument #2 ($displayLocale) must not contain any null bytes locale='uk-ua_CALIFORNIA@currency=;currency=GRN' disp_locale=en : display_script= disp_locale=fr : display_script= diff --git a/ext/intl/tests/locale_get_region.phpt b/ext/intl/tests/locale_get_region.phpt index f843397bdb6cf..0c3c1655deb3a 100644 --- a/ext/intl/tests/locale_get_region.phpt +++ b/ext/intl/tests/locale_get_region.phpt @@ -76,6 +76,12 @@ function ut_main() $res_str .= "\n"; } + try { + ut_loc_get_region("a-\0DE"); + } catch (\ValueError $e) { + echo $e->getMessage(). PHP_EOL; + } + return $res_str; } @@ -85,6 +91,8 @@ ut_run(); ?> --EXPECTF-- +Locale::getRegion(): Argument #1 ($locale) must not contain any null bytes +locale_get_region(): Argument #1 ($locale) must not contain any null bytes uk-ua_CALIFORNIA@currency=;currency=GRN: region='UA' root: region='' uk@currency=EURO: region='' diff --git a/ext/intl/tests/locale_get_script.phpt b/ext/intl/tests/locale_get_script.phpt index e00c83626d5bf..52e0a4155a971 100644 --- a/ext/intl/tests/locale_get_script.phpt +++ b/ext/intl/tests/locale_get_script.phpt @@ -74,6 +74,12 @@ function ut_main() $res_str .= "\n"; } + try { + ut_loc_get_script("de\0-419-DE"); + } catch (\ValueError $e) { + echo $e->getMessage(). PHP_EOL; + } + return $res_str; } @@ -83,6 +89,8 @@ ut_run(); ?> --EXPECT-- +Locale::getScript(): Argument #1 ($locale) must not contain any null bytes +locale_get_script(): Argument #1 ($locale) must not contain any null bytes uk-ua_CALIFORNIA@currency=;currency=GRN: script='' root: script='' uk@currency=EURO: script='' diff --git a/ext/intl/tests/locale_is_right_to_left.phpt b/ext/intl/tests/locale_is_right_to_left.phpt index c586582d42225..c9e4323ff7442 100644 --- a/ext/intl/tests/locale_is_right_to_left.phpt +++ b/ext/intl/tests/locale_is_right_to_left.phpt @@ -8,9 +8,15 @@ var_dump(Locale::isRightToLeft("en-US")); var_dump(Locale::isRightToLeft("\INVALID\\")); var_dump(Locale::isRightToLeft("")); var_dump(Locale::isRightToLeft("ar")); +try { + Locale::isRightToLeft("a\0r"); +} catch (\ValueError $e) { + echo $e->getMessage(); +} ?> --EXPECT-- bool(false) bool(false) bool(false) bool(true) +Locale::isRightToLeft(): Argument #1 ($locale) must not contain any null bytes diff --git a/ext/intl/tests/locale_lookup_variant3.phpt b/ext/intl/tests/locale_lookup_variant3.phpt index c1741a0ed9dd4..b13a54139f443 100644 --- a/ext/intl/tests/locale_lookup_variant3.phpt +++ b/ext/intl/tests/locale_lookup_variant3.phpt @@ -59,6 +59,24 @@ function ut_main() } + try { + ut_loc_locale_lookup(["de\0-DE"], "de-DE", false, "en-US"); + } catch (\ValueError $e) { + echo $e->getMessage(). PHP_EOL; + } + + try { + ut_loc_locale_lookup(["de-DE"], "de-D\0E", true, "en-US"); + } catch (\ValueError $e) { + echo $e->getMessage(). PHP_EOL; + } + + try { + ut_loc_locale_lookup(["de-DE"], "de-DE", true, "e\0n-US"); + } catch (\ValueError $e) { + echo $e->getMessage(). PHP_EOL; + } + $res_str .= "\n"; return $res_str; @@ -69,6 +87,12 @@ ut_run(); ?> --EXPECT-- +Locale::lookup(): Argument #2 ($locale) must not contain any null bytes +Locale::lookup(): Argument #2 ($locale) must not contain any null bytes +Locale::lookup(): Argument #4 ($defaultLocale) must not contain any null bytes +locale_lookup(): Argument #2 ($locale) must not contain any null bytes +locale_lookup(): Argument #2 ($locale) must not contain any null bytes +locale_lookup(): Argument #4 ($defaultLocale) must not contain any null bytes -------------- loc_range:de-de lang_tags: de-DEVA,de-DE-1996,de-DE,zh_Hans,de-CH-1996,sl_IT,sl_IT_nedis-a-kirti-x-xyz,sl_IT_rozaj,sl_IT_NEDIS_ROJAZ_1901,i-enochian,sgn-CH-de,art-lojban,i-lux,art-lojban,jbo,en_sl_IT,zh-Hant-CN-x-prv1-prv2 diff --git a/ext/intl/tests/locale_set_default.phpt b/ext/intl/tests/locale_set_default.phpt index 0f690aabc2965..6108f370274bb 100644 --- a/ext/intl/tests/locale_set_default.phpt +++ b/ext/intl/tests/locale_set_default.phpt @@ -86,6 +86,12 @@ function ut_main() $res_str .= "\n"; } + try { + ut_loc_set_default("a-\0DE"); + } catch (\ValueError $e) { + echo $e->getMessage(), PHP_EOL; + } + return $res_str; } @@ -95,6 +101,8 @@ ut_run(); ?> --EXPECT-- +Locale::setDefault(): Argument #1 ($locale) must not contain any null bytes +locale_set_default(): Argument #1 ($locale) must not contain any null bytes uk-ua_CALIFORNIA@currency=;currency=GRN: set locale 'uk-ua_CALIFORNIA@currency=;currency=GRN' root: set locale 'root' uk@currency=EURO: set locale 'uk@currency=EURO'