Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions NEWS
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,10 @@ PHP NEWS
left busy for the next fetch, and rows delivered from a result another
statement took over. (KentarouTakeda)

- Intl:
. Fixed a use-after-free when IntlRuleBasedBreakIterator is constructed
from compiled rules and the source string is released. (iliaal)

- Phar:
. Fixed Phar archives being automatically detected when ".phar" only occurs
in a directory name or is not a filename extension in an included file's
Expand Down
8 changes: 8 additions & 0 deletions ext/intl/breakiterator/breakiterator_class.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,9 @@ static zend_object *BreakIterator_clone_obj(zend_object *object)
} else {
bio_new->biter = new_biter;
ZVAL_COPY(&bio_new->text, &bio_orig->text);
if (bio_orig->compiled_rules) {
bio_new->compiled_rules = zend_string_copy(bio_orig->compiled_rules);
}
}
} else {
zend_throw_error(NULL, "Cannot clone uninitialized BreakIterator");
Expand Down Expand Up @@ -163,6 +166,7 @@ static void breakiterator_object_init(BreakIterator_object *bio)
{
intl_error_init(BREAKITER_ERROR_P(bio));
bio->biter = NULL;
bio->compiled_rules = NULL;
ZVAL_UNDEF(&bio->text);
}
/* }}} */
Expand All @@ -177,6 +181,10 @@ static void BreakIterator_objects_free(zend_object *object)
delete bio->biter;
bio->biter = NULL;
}
if (bio->compiled_rules) {
zend_string_release(bio->compiled_rules);
bio->compiled_rules = NULL;
}
intl_error_reset(BREAKITER_ERROR_P(bio));

zend_object_std_dtor(&bio->zo);
Expand Down
2 changes: 2 additions & 0 deletions ext/intl/breakiterator/breakiterator_class.h
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ typedef struct {
// current text
zval text;

zend_string *compiled_rules;

zend_object zo;
} BreakIterator_object;

Expand Down
3 changes: 3 additions & 0 deletions ext/intl/breakiterator/rulebasedbreakiterator_methods.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,9 @@ U_CFUNC PHP_METHOD(IntlRuleBasedBreakIterator, __construct)
}

breakiterator_object_create(object, rbbi, false);
if (compiled) {
Z_INTL_BREAKITERATOR_P(object)->compiled_rules = zend_string_copy(rules);
}
}

U_CFUNC PHP_METHOD(IntlRuleBasedBreakIterator, getRules)
Expand Down
50 changes: 50 additions & 0 deletions ext/intl/tests/rbbiter_compiled_rules_lifetime.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
--TEST--
IntlRuleBasedBreakIterator compiled rules outlive the source string
--EXTENSIONS--
intl
--SKIPIF--
<?php if (version_compare(INTL_ICU_VERSION, '68.1') < 0) die('skip for ICU >= 68.1'); ?>
--FILE--
<?php

$rules = <<<RULES
\$LN = [[:letter:] [:number:]];
\$S = [.;,:];

!!forward;
\$LN+ {1};
\$S+ {42};
!!reverse;
\$LN+ {1};
\$S+ {42};
!!safe_forward;
!!safe_reverse;
RULES;

$src = new IntlRuleBasedBreakIterator($rules);
$it = new IntlRuleBasedBreakIterator($src->getBinaryRules(), true);
unset($src);

$it->setText('ab,cd');
echo $it->first(), "\n";
while (true) {
$n = $it->next();
if ($n === IntlBreakIterator::DONE) {
break;
}
echo $n, "\n";
}

$clone = clone $it;
$clone->setText('xy');
echo $clone->first(), "\n";
echo $clone->next(), "\n";

?>
--EXPECT--
0
2
3
5
0
2
Loading