Skip to content

Fix leak when iterating IntlBreakIterator parts iterators - #23464

Open
iliaal wants to merge 1 commit into
php:PHP-8.4from
iliaal:fix/intl-enum-current-leak-84
Open

Fix leak when iterating IntlBreakIterator parts iterators#23464
iliaal wants to merge 1 commit into
php:PHP-8.4from
iliaal:fix/intl-enum-current-leak-84

Conversation

@iliaal

@iliaal iliaal commented Aug 25, 2026

Copy link
Copy Markdown
Contributor

Iterating IntlBreakIterator::getPartsIterator() leaked memory on every loop because the IntlPartsIterator held a counted self-reference through its embedded zend_object_iterator's wrapping_obj, so refcount destruction could never complete, and the retained current element was additionally never released at iterator destruction. zoi_with_current_dtor() now invalidates the current element and the parts iterator leaves wrapping_obj UNDEF as the plain BreakIterator iterator already does, making teardown deterministic; the string enumeration iterator keeps its self-reference because move_forward and rewind need the owner for error handling.


((zoi_with_current*)ii->iterator)->destroy_it = _breakiterator_parts_destroy_it;
ZVAL_OBJ_COPY(&((zoi_with_current*)ii->iterator)->wrapping_obj, Z_OBJ_P(object));
ZVAL_UNDEF(&((zoi_with_current*)ii->iterator)->wrapping_obj);

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.

the teardown in zoi_with_current_dtor() needs to move into the iterator's own dtor, otherwise _breakiterator_parts_destroy_it() releases the BreakIterator mid iteration and zoi_bit->bio dangles. Iterating a temporary parts iterator segfaults on the second element today, and a test doing that (iterating a call result directly, not a variable) should come with it.

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.

Moved destroy_it into the iterator dtor. Test iterates a call result.

--TEST--
IntlPartsIterator must not retain the current element after destruction
--SKIPIF--
<?php if (!extension_loaded('intl')) die('skip intl extension not available'); ?>

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.

this is old fashion tests, new tests should not do this.

getPartsIterator() leaked because wrapping_obj was a counted
self-reference, so the iterator never reached destruction and the
current element was retained. wrapping_obj stays UNDEF; current and
the backing BreakIterator are released from the iterator dtor, not
the IntlIterator object dtor, so iterating a temporary parts
iterator does not dangle. The string enumeration iterator still
self-references because move_forward/rewind need the owner.

Closes phpGH-23464
@iliaal
iliaal force-pushed the fix/intl-enum-current-leak-84 branch from be7ba61 to 1c43e15 Compare August 26, 2026 11:46
@iliaal
iliaal requested a review from devnexen August 26, 2026 11:57
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