Skip to content

ext/intl: Reject unconstructed Collator in attribute and strength methods - #23797

Open
iliaal wants to merge 1 commit into
php:PHP-8.4from
iliaal:fix/collator-attr-uninit-84
Open

iliaal wants to merge 1 commit into
php:PHP-8.4from
iliaal:fix/collator-attr-uninit-84

Conversation

@iliaal

@iliaal iliaal commented Sep 20, 2026

Copy link
Copy Markdown
Member

Collator::getAttribute(), setAttribute(), getStrength() and setStrength() reach ICU with a NULL collator when a subclass skips parent::__construct(), so getAttribute() returns -1 and setStrength() reports success on an object that cannot collate. The four methods that already check, compare(), getLocale(), sort() and getSortKey(), throw "Object not initialized"; this applies the same guard to the remaining four. COLLATOR_METHOD_FETCH_OBJECT does not check on its own.

@LamentXU123 LamentXU123 left a comment

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.

Is it possible to abstract these stuff to a shared helper?

@LamentXU123

Copy link
Copy Markdown
Member

i.e.

static zend_always_inline zend_result collator_check_initialized(
	Collator_object *co)
{
	ZEND_ASSERT(co != NULL);

	if (UNEXPECTED(co->ucoll == NULL)) {
		intl_error_set_code(NULL, COLLATOR_ERROR_CODE(co));
		intl_errors_set_custom_msg(
			COLLATOR_ERROR_P(co), "Object not initialized", 0);
		zend_throw_error(NULL, "Object not initialized");
		return FAILURE;
	}

	return SUCCESS;
}

@iliaal
iliaal force-pushed the fix/collator-attr-uninit-84 branch from 16a26e7 to a5ba67d Compare September 20, 2026 16:38
@iliaal

iliaal commented Sep 20, 2026

Copy link
Copy Markdown
Member Author

Added collator_check_initialized() to collator_class.h and routed the four guards through it. Kept your ZEND_ASSERT: co comes from Z_INTL_COLLATOR_P(), which is offset arithmetic on an already-validated zend_object, so the !co half I had was never reachable.

Left the pre-existing guards in collator_compare.c, collator_locale.c and collator_sort.c alone for now. The one in collator_sort.c uses a shorter shape that throws without setting the intl error state, so folding those in would change observable error state rather than just dedupe.

@LamentXU123 LamentXU123 left a comment

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.

Several small suggestions.
a. Can we utilize this helper in other functions? compare()? getLocale()?
b. Could you also extend the test to cover collator_get_attribute(), collator_set_attribute(), collator_get_strength(), and collator_set_strength() with an unconstructed Collator?

@iliaal
iliaal force-pushed the fix/collator-attr-uninit-84 branch from a5ba67d to d43a043 Compare September 20, 2026 18:40
@iliaal

iliaal commented Sep 20, 2026

Copy link
Copy Markdown
Member Author

Done for compare(), getLocale(), sortWithSortKeys() and getSortKey(). Left the one in collator_sort_internal() (sort() and asort()): it only throws, where the helper also sets the object's intl error and, under intl.error_level, emits a warning. On an unconstructed Collator today, collator_get_error_message() returns "U_ZERO_ERROR" after a caught sort() against "Object not initialized: U_ZERO_ERROR" after compare(). That's a behavior change rather than a dedupe, so I'd keep it separate.

Test now covers collator_get_attribute(), collator_set_attribute(), collator_get_strength() and collator_set_strength() alongside the methods.

@devnexen

Copy link
Copy Markdown
Member

can collator_sort_internal() be taking care of while at it ?

…hods

Collator::getAttribute(), setAttribute(), getStrength() and setStrength()
dereferenced a NULL ICU collator when called on an object whose constructor
skipped parent::__construct(), returning bogus values instead of failing,
while compare(), getLocale(), sort() and getSortKey() already throw
"Object not initialized". Apply the same guard to the four remaining
methods through a collator_check_initialized() helper, which also replaces
the five existing copies. sort() and asort() carried a shorter variant that
threw without recording the failure, so they now set the object's intl error
state like every other method.

Closes phpGH-23797
@iliaal
iliaal force-pushed the fix/collator-attr-uninit-84 branch from d43a043 to 014133d Compare September 20, 2026 19:21
@iliaal

iliaal commented Sep 20, 2026

Copy link
Copy Markdown
Member Author

Done in 014133d. One behavior change to note: the helper also sets the intl error state, so sort() and asort() now emit the diagnostic under a non-default intl.error_level, and at E_ERROR that turns a catchable Error into a fatal. compare(), getLocale(), getSortKey() and sortWithSortKeys() already do that on 8.4, so this closes a gap rather than opening one, but it is still a behavior change on a stable branch. Under the default settings the only difference is collator_get_error_message() returning "Object not initialized: U_ZERO_ERROR" instead of "U_ZERO_ERROR".

If you'd rather 8.4 kept to the four attribute methods and the sort_internal() conversion went to master, I'll split it (I think that is best).

@devnexen

Copy link
Copy Markdown
Member

indeed

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.

3 participants