From 30b6b69a2acaa41d3de9e7d7b1e434f21efb9885 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9CLamentXU123=E2=80=9D?= <108666168+LamentXU123@users.noreply.github.com> Date: Mon, 1 Jun 2026 20:20:29 +0800 Subject: [PATCH 1/2] ext/intl: Fix UConverter::transcode() substitution length truncation --- NEWS | 2 ++ UPGRADING | 3 +++ ext/intl/converter/converter.cpp | 14 +++++++++++-- .../uconverter_transcode_subst_length.phpt | 20 +++++++++++++++++++ 4 files changed, 37 insertions(+), 2 deletions(-) create mode 100644 ext/intl/tests/uconverter_transcode_subst_length.phpt diff --git a/NEWS b/NEWS index 3c4d6993f46c..5fc78a655157 100644 --- a/NEWS +++ b/NEWS @@ -73,6 +73,8 @@ PHP NEWS types. (Weilin Du) . Fixed MessageFormatter::parse() and parseMessage() returning PHP_INT_MIN as float rather than int on 64-bit platforms. (Weilin Du) + . Fixed UConverter::transcode() silently truncating from_subst and to_subst + option lengths greater than 127 bytes. (Weilin Du) - JSON: . Enriched JSON last error / exception message with error location. diff --git a/UPGRADING b/UPGRADING index c996f963a34f..5e3e86ebad9f 100644 --- a/UPGRADING +++ b/UPGRADING @@ -44,6 +44,9 @@ PHP 8.6 UPGRADE NOTES int, rather than float, on 64-bit platforms when parsing integer values. . The $type parameter of IntlBreakIterator::getPartsIterator() has been changed from string to int to match the underlying implementation. + . UConverter::transcode() now rejects from_subst and to_subst option values + longer than 127 bytes instead of silently truncating the length before + passing it to ICU. - PCNTL: . pcntl_alarm() now raises a ValueError if the seconds argument is diff --git a/ext/intl/converter/converter.cpp b/ext/intl/converter/converter.cpp index 597746b8faca..88f05136bbbd 100644 --- a/ext/intl/converter/converter.cpp +++ b/ext/intl/converter/converter.cpp @@ -682,6 +682,16 @@ static zend_string* php_converter_do_convert(UConverter *dest_cnv, } /* }}} */ +static void php_converter_set_subst_chars(UConverter *cnv, zend_string *subst, UErrorCode *error) +{ + if (ZSTR_LEN(subst) > SCHAR_MAX) { + *error = U_ILLEGAL_ARGUMENT_ERROR; + return; + } + + ucnv_setSubstChars(cnv, ZSTR_VAL(subst), (int8_t) ZSTR_LEN(subst), error); +} + /* {{{ */ #define UCNV_REASON_CASE(v) case (UCNV_ ## v) : RETURN_STRINGL( "REASON_" #v , sizeof( "REASON_" #v ) - 1); PHP_METHOD(UConverter, reasonText) { @@ -761,13 +771,13 @@ PHP_METHOD(UConverter, transcode) { (tmpzval = zend_hash_str_find_deref(Z_ARRVAL_P(options), "from_subst", sizeof("from_subst") - 1)) != NULL && Z_TYPE_P(tmpzval) == IS_STRING) { error = U_ZERO_ERROR; - ucnv_setSubstChars(src_cnv, Z_STRVAL_P(tmpzval), Z_STRLEN_P(tmpzval) & 0x7F, &error); + php_converter_set_subst_chars(src_cnv, Z_STR_P(tmpzval), &error); } if (U_SUCCESS(error) && (tmpzval = zend_hash_str_find_deref(Z_ARRVAL_P(options), "to_subst", sizeof("to_subst") - 1)) != NULL && Z_TYPE_P(tmpzval) == IS_STRING) { error = U_ZERO_ERROR; - ucnv_setSubstChars(dest_cnv, Z_STRVAL_P(tmpzval), Z_STRLEN_P(tmpzval) & 0x7F, &error); + php_converter_set_subst_chars(dest_cnv, Z_STR_P(tmpzval), &error); } } diff --git a/ext/intl/tests/uconverter_transcode_subst_length.phpt b/ext/intl/tests/uconverter_transcode_subst_length.phpt new file mode 100644 index 000000000000..3963f3721407 --- /dev/null +++ b/ext/intl/tests/uconverter_transcode_subst_length.phpt @@ -0,0 +1,20 @@ +--TEST-- +UConverter::transcode() rejects too long substitution strings +--EXTENSIONS-- +intl +--INI-- +intl.use_exceptions=false +--FILE-- + $subst])); +var_dump(intl_get_error_code()); +var_dump(UConverter::transcode('abc', 'UTF-8', 'ASCII', ['to_subst' => $subst])); +var_dump(intl_get_error_code()); +?> +--EXPECT-- +bool(false) +int(1) +bool(false) +int(1) From 94e9941690943186af5948de5d9c545250e00fc6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9CLamentXU123=E2=80=9D?= <108666168+LamentXU123@users.noreply.github.com> Date: Mon, 1 Jun 2026 21:40:24 +0800 Subject: [PATCH 2/2] test error message --- ext/intl/tests/uconverter_transcode_subst_length.phpt | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/ext/intl/tests/uconverter_transcode_subst_length.phpt b/ext/intl/tests/uconverter_transcode_subst_length.phpt index 3963f3721407..93001b16680c 100644 --- a/ext/intl/tests/uconverter_transcode_subst_length.phpt +++ b/ext/intl/tests/uconverter_transcode_subst_length.phpt @@ -9,12 +9,12 @@ intl.use_exceptions=false $subst = str_repeat('A', 129); var_dump(UConverter::transcode('abc', 'UTF-8', 'ASCII', ['from_subst' => $subst])); -var_dump(intl_get_error_code()); +echo intl_get_error_message(), "\n"; var_dump(UConverter::transcode('abc', 'UTF-8', 'ASCII', ['to_subst' => $subst])); -var_dump(intl_get_error_code()); +echo intl_get_error_message(), "\n"; ?> --EXPECT-- bool(false) -int(1) +UConverter::transcode(): returned error 1: U_ILLEGAL_ARGUMENT_ERROR: U_ILLEGAL_ARGUMENT_ERROR bool(false) -int(1) +UConverter::transcode(): returned error 1: U_ILLEGAL_ARGUMENT_ERROR: U_ILLEGAL_ARGUMENT_ERROR