diff options
author | Christoph M. Becker <cmb@php.net> | 2015-06-23 16:50:52 +0200 |
---|---|---|
committer | Christoph M. Becker <cmb@php.net> | 2015-06-23 16:50:52 +0200 |
commit | ec8731c46b40769ceac5242c04197e31477bf3d1 (patch) | |
tree | dcd6d275fd2fd406385fff9bb580ca13964ab50d | |
parent | b559cc98bab23c284dfe4604219d33d2f6cf4c6b (diff) | |
parent | 7953ff4ea3447614adfd5aeff852938af72bbb36 (diff) | |
download | php-git-ec8731c46b40769ceac5242c04197e31477bf3d1.tar.gz |
Merge branch 'PHP-5.6'
* PHP-5.6:
Fixed bug #69864 (Segfault in preg_replace_callback)
-rw-r--r-- | ext/pcre/php_pcre.c | 22 | ||||
-rw-r--r-- | ext/pcre/tests/bug69864.phpt | 36 |
2 files changed, 53 insertions, 5 deletions
diff --git a/ext/pcre/php_pcre.c b/ext/pcre/php_pcre.c index 11b74b45b3..418859f5d2 100644 --- a/ext/pcre/php_pcre.c +++ b/ext/pcre/php_pcre.c @@ -187,13 +187,14 @@ static PHP_MSHUTDOWN_FUNCTION(pcre) /* {{{ static pcre_clean_cache */ static int pcre_clean_cache(zval *data, void *arg) { + pcre_cache_entry *pce = (pcre_cache_entry *) Z_PTR_P(data); int *num_clean = (int *)arg; - if (*num_clean > 0) { + if (*num_clean > 0 && !pce->refcount) { (*num_clean)--; - return 1; + return ZEND_HASH_APPLY_REMOVE; } else { - return 0; + return ZEND_HASH_APPLY_KEEP; } } /* }}} */ @@ -461,6 +462,7 @@ PHPAPI pcre_cache_entry* pcre_get_compiled_regex_cache(zend_string *regex) NULL; new_entry.tables = tables; #endif + new_entry.refcount = 0; rc = pcre_fullinfo(re, extra, PCRE_INFO_CAPTURECOUNT, &new_entry.capture_count); if (rc < 0) { @@ -584,8 +586,10 @@ static void php_do_pcre_match(INTERNAL_FUNCTION_PARAMETERS, int global) /* {{{ * RETURN_FALSE; } + pce->refcount++; php_pcre_match_impl(pce, subject->val, (int)subject->len, return_value, subpats, global, ZEND_NUM_ARGS() >= 4, flags, start_offset); + pce->refcount--; } /* }}} */ @@ -1017,14 +1021,18 @@ PHPAPI zend_string *php_pcre_replace(zend_string *regex, int limit, int *replace_count) { pcre_cache_entry *pce; /* Compiled regular expression */ + zend_string *result; /* Function result */ /* Compile regex or get it from cache. */ if ((pce = pcre_get_compiled_regex_cache(regex)) == NULL) { return NULL; } - - return php_pcre_replace_impl(pce, subject_str, subject, subject_len, replace_val, + pce->refcount++; + result = php_pcre_replace_impl(pce, subject_str, subject, subject_len, replace_val, is_callable_replace, limit, replace_count); + pce->refcount--; + + return result; } /* }}} */ @@ -1660,7 +1668,9 @@ static PHP_FUNCTION(preg_split) RETURN_FALSE; } + pce->refcount++; php_pcre_split_impl(pce, subject->val, (int)subject->len, return_value, (int)limit_val, flags); + pce->refcount--; } /* }}} */ @@ -1967,7 +1977,9 @@ static PHP_FUNCTION(preg_grep) RETURN_FALSE; } + pce->refcount++; php_pcre_grep_impl(pce, input, return_value, flags); + pce->refcount--; } /* }}} */ diff --git a/ext/pcre/tests/bug69864.phpt b/ext/pcre/tests/bug69864.phpt new file mode 100644 index 0000000000..d84862aeda --- /dev/null +++ b/ext/pcre/tests/bug69864.phpt @@ -0,0 +1,36 @@ +--TEST--
+Bug #69864 (Segfault in preg_replace_callback)
+--FILE--
+<?php
+const PREG_CACHE_SIZE = 4096; // this has to be >= the resp. constant in php_pcre.c
+
+var_dump(preg_replace_callback('/a/', function($m) {
+ for ($i = 0; $i < PREG_CACHE_SIZE; $i++) {
+ preg_match('/foo' . $i . 'bar/', '???foo' . $i . 'bar???');
+ }
+ return 'b';
+}, 'aa'));
+var_dump(preg_replace_callback('/a/', function($m) {
+ for ($i = 0; $i < PREG_CACHE_SIZE; $i++) {
+ preg_replace('/foo' . $i . 'bar/', 'baz', '???foo' . $i . 'bar???');
+ }
+ return 'b';
+}, 'aa'));
+var_dump(preg_replace_callback('/a/', function($m) {
+ for ($i = 0; $i < PREG_CACHE_SIZE; $i++) {
+ preg_split('/foo' . $i . 'bar/', '???foo' . $i . 'bar???');
+ }
+ return 'b';
+}, 'aa'));
+var_dump(preg_replace_callback('/a/', function($m) {
+ for ($i = 0; $i < PREG_CACHE_SIZE; $i++) {
+ preg_grep('/foo' . $i . 'bar/', ['???foo' . $i . 'bar???']);
+ }
+ return 'b';
+}, 'aa'));
+?>
+--EXPECT--
+string(2) "bb"
+string(2) "bb"
+string(2) "bb"
+string(2) "bb"
|