Skip to content

[intl] Record the ICU error code on Spoofchecker failures - #23520

Open
iliaal wants to merge 1 commit into
php:masterfrom
iliaal:fix/spoofchecker-icu-failure-master
Open

[intl] Record the ICU error code on Spoofchecker failures#23520
iliaal wants to merge 1 commit into
php:masterfrom
iliaal:fix/spoofchecker-icu-failure-master

Conversation

@iliaal

@iliaal iliaal commented Aug 31, 2026

Copy link
Copy Markdown
Member

Spoofchecker::isSuspicious(), ::areConfusable(), ::areBidiConfusable(), ::setChecks(), ::setAllowedLocales() and ::setAllowedChars() warn on U_FAILURE but never record the code, so intl_get_error_code() still reads U_ZERO_ERROR after a failed call. They now record it, which is what SPOOFCHECKER_CHECK_STATUS in spoofchecker_class.h already prescribes for this class.

setChecks() is the only one of the six with a failure a test can reach, since uspoof_setChecks() rejects check bits outside USPOOF_ALL_CHECKS|USPOOF_AUX_INFO. The check methods are still untestable: ICU substitutes malformed UTF-8 rather than failing, and the only route left needs a string over INT32_MAX.

@@ -46,12 +46,13 @@ U_CFUNC PHP_METHOD(Spoofchecker, isSuspicious)
ret = intl_icu_compat_uspoof_check_utf8(co->uspoof, ZSTR_VAL(text), ZSTR_LEN(text), co->uspoofres, SPOOFCHECKER_ERROR_CODE_P(co));

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

--- a/ext/intl/spoofchecker/spoofchecker_main.cpp
+++ b/ext/intl/spoofchecker/spoofchecker_main.cpp
@@ -46,6 +46,7 @@
      ret = intl_icu_compat_uspoof_check_utf8(co->uspoof, ZSTR_VAL(text), ZSTR_LEN(text), co->uspoofres, SPOOFCHECKER_ERROR_CODE_P(co));

      if (U_FAILURE(SPOOFCHECKER_ERROR_CODE(co))) {
+             intl_error_set_code(NULL, SPOOFCHECKER_ERROR_CODE(co));
              php_error_docref(NULL, E_WARNING, "(%d) %s", SPOOFCHECKER_ERROR_CODE(co), u_errorName(SPOOFCHECKER_ERROR_CODE(co)));

              if (intl_icu_compat_uspoof_check_result_mismatch(co->uspoofres, ret, &errmask, SPOOFCHECKER_ERROR_CODE_P(co))) {
@@ -83,6 +84,7 @@
              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));
      }
      if (U_FAILURE(SPOOFCHECKER_ERROR_CODE(co))) {
+             intl_error_set_code(NULL, SPOOFCHECKER_ERROR_CODE(co));
              php_error_docref(NULL, E_WARNING, "(%d) %s", SPOOFCHECKER_ERROR_CODE(co), u_errorName(SPOOFCHECKER_ERROR_CODE(co)));
              RETURN_TRUE;
      }
@@ -355,6 +357,7 @@
              ret = uspoof_areBidiConfusableUTF8(co->uspoof, (UBiDiDirection)direction, ZSTR_VAL(s1), (int32_t)ZSTR_LEN(s1), ZSTR_VAL(s2), (int32_t)ZSTR_LEN(s2), SPOOFCHECKER_ERROR_CODE_P(co));
      }
      if (U_FAILURE(SPOOFCHECKER_ERROR_CODE(co))) {
+             intl_error_set_code(NULL, SPOOFCHECKER_ERROR_CODE(co));
              php_error_docref(NULL, E_WARNING, "(%d) %s", SPOOFCHECKER_ERROR_CODE(co), u_errorName(SPOOFCHECKER_ERROR_CODE(co)));
              RETURN_TRUE;
      }

also I do not see the reasoning behing the RETURN_TRUE->RETURN_FALSE changes ?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Your first hunk is already in the patch, so the only difference is RETURN_FALSE. true from isSuspicious() means suspicious, and the U_FAILURE branch returns before $errorCode is assigned, so the caller cannot separate a detection from an ICU failure. SPOOFCHECKER_CHECK_STATUS in spoofchecker_class.h already spells out that contract, it just has no call sites.

Counter is that true is fail-closed for if ($sc->isSuspicious($name)) { reject(); }. Your call, I'll keep only the error-code recording if you prefer that.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes the error recording change alone is enough.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

after looking, it seems setChecks()/setAllowedLocales() could be fixed too ?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added, plus setAllowedChars() which had the same gap. setChecks(1 << 20) turned out to be reachable, so there's a test and a NEWS entry now; setAllowedLocales() I still could not make fail from userland.

iliaal added a commit to iliaal/php-src that referenced this pull request Aug 31, 2026
Spoofchecker::isSuspicious(), ::areConfusable() and ::areBidiConfusable()
warn on U_FAILURE but never store the code, so intl_get_error_code() still
reads U_ZERO_ERROR after a failed check. Record it so a caller can tell an
internal ICU failure from a real detection. The true return is unchanged.

Closes phpGH-23520
@iliaal
iliaal force-pushed the fix/spoofchecker-icu-failure-master branch from 452bfe6 to d98cac3 Compare August 31, 2026 12:12
@iliaal iliaal changed the title [intl] Return false from Spoofchecker checks on ICU failure [intl] Record the ICU error code on Spoofchecker check failure Aug 31, 2026
Spoofchecker::isSuspicious(), ::areConfusable(), ::areBidiConfusable(),
::setChecks(), ::setAllowedLocales() and ::setAllowedChars() warn on
U_FAILURE but never record the code, so intl_get_error_code() still reads
U_ZERO_ERROR after a failed call. They now record it, which is what
SPOOFCHECKER_CHECK_STATUS in spoofchecker_class.h already prescribes for
this class.

Closes phpGH-23520
@iliaal
iliaal force-pushed the fix/spoofchecker-icu-failure-master branch from d98cac3 to 4006162 Compare August 31, 2026 19:51
@iliaal iliaal changed the title [intl] Record the ICU error code on Spoofchecker check failure [intl] Record the ICU error code on Spoofchecker failures Aug 31, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants