[php-src] PHP-8.5: Bound getSingletonPos before reading the next separator (#23350)
Ilia Alshanetsky via Weilin Du <[email protected]>
| Newsgroups | gmane.comp.php.cvs.general |
|---|---|
| Message-ID | <[email protected]> |
Author: Ilia Alshanetsky (iliaal) Committer: Weilin Du (LamentXU123) Date: 2026-08-18T22:38:26+08:00 Commit: https://github.com/php/php-src/commit/9f23a4de630864749da25da803080e090de93ab1 Raw diff: https://github.com/php/php-src/commit/9f23a4de630864749da25da803080e090de93ab1.diff Bound getSingletonPos before reading the next separator (#23350) getSingletonPos() read str[i+2] after the last '-' or '_' in a locale, one byte past the NUL for strings such as "en-". Require i+2 to be inside the string before testing it for another separator. Output otherwise matches the unfixed parser unless that heap byte happens to be another separator, which is why GH-22498 was previously closed. Also audit getStrrtokenPos() and both callers, get_icu_value_internal() and get_private_subtags(); their bounds handling is already safe. Co-authored-by: Xuyang Zhang <[email protected]> Closes #23350 Changed paths: A ext/intl/tests/locale_parse_trailing_separator.phpt M NEWS M ext/intl/locale/locale_methods.c Diff: diff --git a/NEWS b/NEWS index 5ab1602dbd69..4cafbbd5a900 100644 --- a/NEWS +++ b/NEWS @@ -15,6 +15,8 @@ PHP NEWS the ICU constructor adopts the TimeZone. (iliaal) . Fixed bug GH-23094 (NumberFormatter parsing offsets use UTF-16 positions for UTF-8 strings). (ColumbusLabs) + . Fixed Locale::parseLocale() reading past a trailing '-' or '_'. + (iliaal, Xuyang Zhang) - Opcache: . Fixed opcache.protect_memory race under ZTS. (realFlowControl) diff --git a/ext/intl/locale/locale_methods.c b/ext/intl/locale/locale_methods.c index b5d48257338a..e3894b6f28fb 100644 --- a/ext/intl/locale/locale_methods.c +++ b/ext/intl/locale/locale_methods.c @@ -279,7 +279,7 @@ static zend_off_t getSingletonPos(const char* str) break; } else { /* delimiter found; check for singleton */ - if( isIDSeparator(*(str+i+2)) ){ + if( (size_t)i + 2 < len && isIDSeparator(*(str+i+2)) ){ /* a singleton; so send the position of separator before singleton */ result = i+1; break; diff --git a/ext/intl/tests/locale_parse_trailing_separator.phpt b/ext/intl/tests/locale_parse_trailing_separator.phpt new file mode 100644 index 000000000000..96b2139b72c3 --- /dev/null +++ b/ext/intl/tests/locale_parse_trailing_separator.phpt @@ -0,0 +1,45 @@ +--TEST-- +Locale::parseLocale() does not read past a trailing '-' or '_' +--EXTENSIONS-- +intl +--FILE-- +<?php +/* Enough lengths that the byte past the end clears the allocation. */ +foreach (['-', '_'] as $sep) { + for ($len = 1; $len <= 64; $len++) { + Locale::parseLocale(str_repeat('a', $len) . $sep); + } +} + +$locales = [ + 'en-', + 'foo-', + 'en_US-', + 'en_', + 'de-CH-x-', +]; + +foreach ($locales as $locale) { + echo $locale, ': '; + var_export(Locale::parseLocale($locale)); + echo "\n"; +} +?> +--EXPECT-- +en-: array ( + 'language' => 'en', +) +foo-: array ( + 'language' => 'foo', +) +en_US-: array ( + 'language' => 'en', + 'region' => 'US', +) +en_: array ( + 'language' => 'en', +) +de-CH-x-: array ( + 'language' => 'de', + 'region' => 'CH', +)