Skip to content

Commit 25466f7

Browse files
committed
Merge branch 'PHP-8.5'
* PHP-8.5: Keep compiled RuleBasedBreakIterator rules alive for the iterator
2 parents f16b1d5 + 3f5baab commit 25466f7

5 files changed

Lines changed: 92 additions & 0 deletions

File tree

‎NEWS‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ PHP NEWS
1111
results. (iliaal)
1212
. Fixed a leak in Locale::getKeywords() when a keyword value cannot be
1313
read. (iliaal)
14+
. Fixed a use-after-free when IntlRuleBasedBreakIterator is constructed
15+
from compiled rules. (iliaal)
1416

1517
- PDO_PGSQL:
1618
. Added Pdo\Pgsql::ATTR_CHUNK_SIZE to fetch a result set in chunks of the

‎ext/intl/breakiterator/breakiterator_class.cpp‎

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,9 @@ static zend_object *BreakIterator_clone_obj(zend_object *object)
109109
} else {
110110
bio_new->biter = new_biter;
111111
ZVAL_COPY(&bio_new->text, &bio_orig->text);
112+
if (bio_orig->compiled_rules) {
113+
bio_new->compiled_rules = zend_string_copy(bio_orig->compiled_rules);
114+
}
112115
}
113116
} else {
114117
zend_throw_error(NULL, "Cannot clone uninitialized BreakIterator");
@@ -163,6 +166,7 @@ static void breakiterator_object_init(BreakIterator_object *bio)
163166
{
164167
intl_error_init(BREAKITER_ERROR_P(bio));
165168
bio->biter = NULL;
169+
bio->compiled_rules = NULL;
166170
ZVAL_UNDEF(&bio->text);
167171
}
168172
/* }}} */
@@ -177,6 +181,10 @@ static void BreakIterator_objects_free(zend_object *object)
177181
delete bio->biter;
178182
bio->biter = NULL;
179183
}
184+
if (bio->compiled_rules) {
185+
zend_string_release(bio->compiled_rules);
186+
bio->compiled_rules = NULL;
187+
}
180188
intl_error_reset(BREAKITER_ERROR_P(bio));
181189

182190
zend_object_std_dtor(&bio->zo);

‎ext/intl/breakiterator/breakiterator_class.h‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,8 @@ typedef struct {
3838
// current text
3939
zval text;
4040

41+
zend_string *compiled_rules;
42+
4143
zend_object zo;
4244
} BreakIterator_object;
4345

‎ext/intl/breakiterator/rulebasedbreakiterator_methods.cpp‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,9 @@ U_CFUNC PHP_METHOD(IntlRuleBasedBreakIterator, __construct)
8787
}
8888

8989
breakiterator_object_create(object, rbbi, false);
90+
if (compiled) {
91+
Z_INTL_BREAKITERATOR_P(object)->compiled_rules = zend_string_copy(rules);
92+
}
9093
}
9194

9295
U_CFUNC PHP_METHOD(IntlRuleBasedBreakIterator, getRules)
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
--TEST--
2+
IntlRuleBasedBreakIterator compiled rules outlive the source string
3+
--EXTENSIONS--
4+
intl
5+
--SKIPIF--
6+
<?php if (version_compare(INTL_ICU_VERSION, '68.1') < 0) die('skip for ICU >= 68.1'); ?>
7+
--FILE--
8+
<?php
9+
10+
$rules = <<<RULES
11+
\$LN = [[:letter:] [:number:]];
12+
\$S = [.;,:];
13+
14+
!!forward;
15+
\$LN+ {1};
16+
\$S+ {42};
17+
!!reverse;
18+
\$LN+ {1};
19+
\$S+ {42};
20+
!!safe_forward;
21+
!!safe_reverse;
22+
RULES;
23+
24+
$src = new IntlRuleBasedBreakIterator($rules);
25+
$len = strlen($src->getBinaryRules());
26+
27+
$it = new IntlRuleBasedBreakIterator($src->getBinaryRules(), true);
28+
unset($src);
29+
30+
/* ICU aliases the buffer it was built from, so the freed rules have to be
31+
reclaimed and overwritten for the iterator below to read stale bytes. */
32+
$ballast = [];
33+
for ($i = 0; $i < 16; $i++) {
34+
$ballast[] = str_repeat("\xCC", $len);
35+
}
36+
37+
$it->setText('ab,cd');
38+
echo $it->first(), "\n";
39+
while (true) {
40+
$n = $it->next();
41+
if ($n === IntlBreakIterator::DONE) {
42+
break;
43+
}
44+
echo $n, "\n";
45+
}
46+
47+
$clone = clone $it;
48+
unset($it);
49+
$ballast[] = str_repeat("\xDD", $len);
50+
$clone->setText('xy');
51+
echo $clone->first(), "\n";
52+
echo $clone->next(), "\n";
53+
54+
$src = new IntlRuleBasedBreakIterator($rules);
55+
$it = new IntlRuleBasedBreakIterator($src->getBinaryRules(), true);
56+
unset($src);
57+
for ($i = 0; $i < 16; $i++) {
58+
$ballast[] = str_repeat("\xEE", $len);
59+
}
60+
$it->setText('ab,cd');
61+
$parts = $it->getPartsIterator();
62+
unset($it);
63+
foreach ($parts as $p) {
64+
echo $p, "\n";
65+
}
66+
67+
?>
68+
--EXPECT--
69+
0
70+
2
71+
3
72+
5
73+
0
74+
2
75+
ab
76+
,
77+
cd

0 commit comments

Comments
 (0)