Skip to content

Commit be7ba61

Browse files
committed
[intl] fix leak when iterating IntlBreakIterator parts iterators
Iterating 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 zoi_with_current_dtor() never released the retained current element either. zoi_with_current_dtor() now invalidates the current element and the parts iterator no longer addrefs itself (wrapping_obj stays UNDEF as in the plain BreakIterator iterator), making teardown deterministic; the string enumeration iterator keeps its self-reference because move_forward/rewind need the owner for error handling.
1 parent b2956e0 commit be7ba61

4 files changed

Lines changed: 27 additions & 1 deletion

File tree

NEWS

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@ PHP NEWS
2525
wrong argument in error messages. (Weilin Du)
2626

2727
- Intl:
28+
. Fixed a memory leak when iterating IntlBreakIterator::getPartsIterator()
29+
results. (iliaal)
2830
. Fixed a double-free when IntlGregorianCalendar construction fails after
2931
the ICU constructor adopts the TimeZone. (iliaal)
3032
. Fixed bug GH-23094 (NumberFormatter parsing offsets use UTF-16 positions

ext/intl/breakiterator/breakiterator_iterators.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -242,7 +242,7 @@ void IntlIterator_from_BreakIterator_parts(zval *break_iter_zv,
242242
ii->iterator->index = 0;
243243

244244
((zoi_with_current*)ii->iterator)->destroy_it = _breakiterator_parts_destroy_it;
245-
ZVAL_OBJ_COPY(&((zoi_with_current*)ii->iterator)->wrapping_obj, Z_OBJ_P(object));
245+
ZVAL_UNDEF(&((zoi_with_current*)ii->iterator)->wrapping_obj);
246246
ZVAL_UNDEF(&((zoi_with_current*)ii->iterator)->current);
247247

248248
((zoi_break_iter_parts*)ii->iterator)->bio = Z_INTL_BREAKITERATOR_P(break_iter_zv);

ext/intl/common/common_enum.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ void zoi_with_current_dtor(zend_object_iterator *iter)
3737
zoi_with_current *zoiwc = (zoi_with_current*)iter;
3838
zval_ptr_dtor(&zoiwc->wrapping_obj);
3939
ZVAL_UNDEF(&zoiwc->wrapping_obj);
40+
iter->funcs->invalidate_current(iter);
4041
}
4142

4243
U_CFUNC zend_result zoi_with_current_valid(zend_object_iterator *iter)
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
--TEST--
2+
IntlPartsIterator must not retain the current element after destruction
3+
--SKIPIF--
4+
<?php if (!extension_loaded('intl')) die('skip intl extension not available'); ?>
5+
--INI--
6+
memory_limit=64M
7+
--FILE--
8+
<?php
9+
$bi = IntlBreakIterator::createWordInstance('en');
10+
$bi->setText('hello world foo bar baz');
11+
$m0 = memory_get_usage();
12+
for ($i = 0; $i < 300000; $i++) {
13+
foreach ($bi->getPartsIterator() as $v) {
14+
break;
15+
}
16+
}
17+
$m1 = memory_get_usage();
18+
var_dump($m1 - $m0 < 4 * 1024 * 1024);
19+
?>
20+
21+
--EXPECT--
22+
bool(true)
23+

0 commit comments

Comments
 (0)