Skip to content

Fix use-after-free in Collator::sort() with a mutating comparator#22467

Closed
iliaal wants to merge 1 commit into
php:PHP-8.4from
iliaal:fix/intl-collator-sort-uaf
Closed

Fix use-after-free in Collator::sort() with a mutating comparator#22467
iliaal wants to merge 1 commit into
php:PHP-8.4from
iliaal:fix/intl-collator-sort-uaf

Conversation

@iliaal

@iliaal iliaal commented Jun 26, 2026

Copy link
Copy Markdown
Contributor

Collator::sort() and Collator::asort() with COLLATOR_SORT_REGULAR use each object element's __toString() as the comparison key. If that __toString() appends to the array being sorted (reachable through a reference), the array's backing storage is reallocated while zend_hash_sort() still holds pointers into it, a use-after-free. usort() already avoids this by duplicating the array before sorting; Collator now does the same.

@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.

Looks Good. I think would be better to add a comment here to make sure people know why we are doing this.

Comment thread ext/intl/collator/collator_sort.c
Comment thread ext/intl/collator/collator_sort.c Outdated

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.

nit: now that the manual zend_array_dup provides the isolation, the / in "Oa/|l" is a redundant eager copy when the array is shared by value (usort skips it for the same reason). Micro optimisation but might be worth it wdyt ?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

You're right once the dup runs first. Reordered so the conversion operates on the copy and the original array is never mutated, which lets the / drop like usort does. The two conversion-failure paths free the copy.

collator_sort() and collator_asort() sort the array in place while the
comparator may run a __toString() that appends to the same array through a
reference, reallocating its storage under the running sort. Sort a copy and
swap it back, matching usort().
@iliaal iliaal force-pushed the fix/intl-collator-sort-uaf branch from 8761bca to f494d4e Compare June 26, 2026 17:42
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