diff --git a/NEWS b/NEWS index 9977ea3b3409..01349d6c111f 100644 --- a/NEWS +++ b/NEWS @@ -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 diff --git a/ext/intl/breakiterator/breakiterator_class.cpp b/ext/intl/breakiterator/breakiterator_class.cpp index 4d5793696cbb..fe6eb2749ce6 100644 --- a/ext/intl/breakiterator/breakiterator_class.cpp +++ b/ext/intl/breakiterator/breakiterator_class.cpp @@ -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"); @@ -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); } /* }}} */ @@ -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); diff --git a/ext/intl/breakiterator/breakiterator_class.h b/ext/intl/breakiterator/breakiterator_class.h index cb4f072139ec..38e83da64dae 100644 --- a/ext/intl/breakiterator/breakiterator_class.h +++ b/ext/intl/breakiterator/breakiterator_class.h @@ -38,6 +38,8 @@ typedef struct { // current text zval text; + zend_string *compiled_rules; + zend_object zo; } BreakIterator_object; diff --git a/ext/intl/breakiterator/rulebasedbreakiterator_methods.cpp b/ext/intl/breakiterator/rulebasedbreakiterator_methods.cpp index 4526b9faab13..611144b58ab6 100644 --- a/ext/intl/breakiterator/rulebasedbreakiterator_methods.cpp +++ b/ext/intl/breakiterator/rulebasedbreakiterator_methods.cpp @@ -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) diff --git a/ext/intl/tests/rbbiter_compiled_rules_lifetime.phpt b/ext/intl/tests/rbbiter_compiled_rules_lifetime.phpt new file mode 100644 index 000000000000..c2d9ba9c51a7 --- /dev/null +++ b/ext/intl/tests/rbbiter_compiled_rules_lifetime.phpt @@ -0,0 +1,50 @@ +--TEST-- +IntlRuleBasedBreakIterator compiled rules outlive the source string +--EXTENSIONS-- +intl +--SKIPIF-- += 68.1'); ?> +--FILE-- +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