diff options
author | Xinchen Hui <laruence@gmail.com> | 2016-11-20 15:44:27 +0800 |
---|---|---|
committer | Xinchen Hui <laruence@gmail.com> | 2016-11-20 15:44:27 +0800 |
commit | ebfd93f725eb9b9cfadbbde98047efe76f658da6 (patch) | |
tree | ebe3dc1c85fc4bebcd4f9080f44c75078266168a | |
parent | 274951a4a2a2c5023d9bfd2e8032d64131e38225 (diff) | |
download | php-git-ebfd93f725eb9b9cfadbbde98047efe76f658da6.tar.gz |
Fixed bug #73483 (Segmentation fault on pcre_replace_callback)
-rw-r--r-- | NEWS | 7 | ||||
-rw-r--r-- | ext/pcre/php_pcre.c | 97 | ||||
-rw-r--r-- | ext/pcre/tests/bug73483.phpt | 15 |
3 files changed, 84 insertions, 35 deletions
@@ -17,9 +17,9 @@ PHP NEWS This may be enabled again using envirionment variable USE_ZEND_DTRACE=1. (Dmitry) -- Mysqlnd:
- . Fixed bug #64526 (Add missing mysqlnd.* parameters to php.ini-*). (cmb)
-
+- Mysqlnd: + . Fixed bug #64526 (Add missing mysqlnd.* parameters to php.ini-*). (cmb) + - ODBC: . Fixed bug #73448 (odbc_errormsg returns trash, always 513 bytes). (Anatol) @@ -29,6 +29,7 @@ PHP NEWS . Fixed bug #73546 (Logging for opcache has an empty file name). (mhagstrand) - PCRE: + . Fixed bug #73483 (Segmentation fault on pcre_replace_callback). (Laruence) . Fixed bug #73392 (A use-after-free in zend allocator management). (Laruence) diff --git a/ext/pcre/php_pcre.c b/ext/pcre/php_pcre.c index b1ffe7f228..8601b731f5 100644 --- a/ext/pcre/php_pcre.c +++ b/ext/pcre/php_pcre.c @@ -114,9 +114,6 @@ static void php_free_pcre_cache(zval *data) /* {{{ */ } #if HAVE_SETLOCALE if ((void*)pce->tables) pefree((void*)pce->tables, 1); - if (pce->locale) { - zend_string_release(pce->locale); - } #endif pefree(pce, 1); } @@ -320,27 +317,30 @@ PHPAPI pcre_cache_entry* pcre_get_compiled_regex_cache(zend_string *regex) pcre_cache_entry *pce; pcre_cache_entry new_entry; int rc; + zend_string *key; + +#if HAVE_SETLOCALE + if (BG(locale_string) && + (ZSTR_LEN(BG(locale_string)) != 1 && ZSTR_VAL(BG(locale_string))[0] != 'C')) { + key = zend_string_alloc(ZSTR_LEN(regex) + ZSTR_LEN(BG(locale_string)) + 1, 0); + memcpy(ZSTR_VAL(key), ZSTR_VAL(BG(locale_string)), ZSTR_LEN(BG(locale_string)) + 1); + memcpy(ZSTR_VAL(key) + ZSTR_LEN(BG(locale_string)), ZSTR_VAL(regex), ZSTR_LEN(regex) + 1); + } else +#endif + { + key = regex; + } /* Try to lookup the cached regex entry, and if successful, just pass back the compiled pattern, otherwise go on and compile it. */ - pce = zend_hash_find_ptr(&PCRE_G(pcre_cache), regex); + pce = zend_hash_find_ptr(&PCRE_G(pcre_cache), key); if (pce) { #if HAVE_SETLOCALE - if (pce->locale == BG(locale_string) || - (pce->locale && BG(locale_string) && - ZSTR_LEN(pce->locale) == ZSTR_LEN(BG(locale_string)) && - !memcmp(ZSTR_VAL(pce->locale), ZSTR_VAL(BG(locale_string)), ZSTR_LEN(pce->locale))) || - (!pce->locale && - ZSTR_LEN(BG(locale_string)) == 1 && - ZSTR_VAL(BG(locale_string))[0] == 'C') || - (!BG(locale_string) && - ZSTR_LEN(pce->locale) == 1 && - ZSTR_VAL(pce->locale)[0] == 'C')) { - return pce; + if (key != regex) { + zend_string_release(key); } -#else - return pce; #endif + return pce; } p = ZSTR_VAL(regex); @@ -349,6 +349,11 @@ PHPAPI pcre_cache_entry* pcre_get_compiled_regex_cache(zend_string *regex) get to the end without encountering a delimiter. */ while (isspace((int)*(unsigned char *)p)) p++; if (*p == 0) { +#if HAVE_SETLOCALE + if (key != regex) { + zend_string_release(key); + } +#endif php_error_docref(NULL, E_WARNING, p < ZSTR_VAL(regex) + ZSTR_LEN(regex) ? "Null byte in regex" : "Empty regular expression"); return NULL; @@ -358,6 +363,11 @@ PHPAPI pcre_cache_entry* pcre_get_compiled_regex_cache(zend_string *regex) or a backslash. */ delimiter = *p++; if (isalnum((int)*(unsigned char *)&delimiter) || delimiter == '\\') { +#if HAVE_SETLOCALE + if (key != regex) { + zend_string_release(key); + } +#endif php_error_docref(NULL,E_WARNING, "Delimiter must not be alphanumeric or backslash"); return NULL; } @@ -397,6 +407,11 @@ PHPAPI pcre_cache_entry* pcre_get_compiled_regex_cache(zend_string *regex) } if (*pp == 0) { +#if HAVE_SETLOCALE + if (key != regex) { + zend_string_release(key); + } +#endif if (pp < ZSTR_VAL(regex) + ZSTR_LEN(regex)) { php_error_docref(NULL,E_WARNING, "Null byte in regex"); } else if (start_delimiter == end_delimiter) { @@ -453,13 +468,17 @@ PHPAPI pcre_cache_entry* pcre_get_compiled_regex_cache(zend_string *regex) php_error_docref(NULL,E_WARNING, "Null byte in regex"); } efree(pattern); +#if HAVE_SETLOCALE + if (key != regex) { + zend_string_release(key); + } +#endif return NULL; } } #if HAVE_SETLOCALE - if (BG(locale_string) && - (ZSTR_LEN(BG(locale_string)) != 1 || ZSTR_VAL(BG(locale_string))[0] != 'C')) { + if (key != regex) { tables = pcre_maketables(); } #endif @@ -472,6 +491,11 @@ PHPAPI pcre_cache_entry* pcre_get_compiled_regex_cache(zend_string *regex) tables); if (re == NULL) { +#if HAVE_SETLOCALE + if (key != regex) { + zend_string_release(key); + } +#endif php_error_docref(NULL,E_WARNING, "Compilation failed: %s at offset %d", error, erroffset); efree(pattern); if (tables) { @@ -516,7 +540,7 @@ PHPAPI pcre_cache_entry* pcre_get_compiled_regex_cache(zend_string *regex) * these are supposedly the oldest ones (but not necessarily the least used * ones). */ - if (zend_hash_num_elements(&PCRE_G(pcre_cache)) == PCRE_CACHE_SIZE) { + if (!pce && zend_hash_num_elements(&PCRE_G(pcre_cache)) == PCRE_CACHE_SIZE) { int num_clean = PCRE_CACHE_SIZE / 8; zend_hash_apply_with_argument(&PCRE_G(pcre_cache), pcre_clean_cache, &num_clean); } @@ -527,23 +551,29 @@ PHPAPI pcre_cache_entry* pcre_get_compiled_regex_cache(zend_string *regex) new_entry.preg_options = poptions; new_entry.compile_options = coptions; #if HAVE_SETLOCALE - new_entry.locale = BG(locale_string) ? - ((GC_FLAGS(BG(locale_string)) & IS_STR_PERSISTENT) ? - zend_string_copy(BG(locale_string)) : - zend_string_init(ZSTR_VAL(BG(locale_string)), ZSTR_LEN(BG(locale_string)), 1)) : - NULL; + new_entry.locale = 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) { +#if HAVE_SETLOCALE + if (key != regex) { + zend_string_release(key); + } +#endif php_error_docref(NULL, E_WARNING, "Internal pcre_fullinfo() error %d", rc); return NULL; } rc = pcre_fullinfo(re, extra, PCRE_INFO_NAMECOUNT, &new_entry.name_count); if (rc < 0) { +#if HAVE_SETLOCALE + if (key != regex) { + zend_string_release(key); + } +#endif php_error_docref(NULL, E_WARNING, "Internal pcre_fullinfo() error %d", rc); return NULL; } @@ -556,15 +586,18 @@ PHPAPI pcre_cache_entry* pcre_get_compiled_regex_cache(zend_string *regex) * as hash keys especually for this table. * See bug #63180 */ - if (!ZSTR_IS_INTERNED(regex) || !(GC_FLAGS(regex) & IS_STR_PERMANENT)) { - zend_string *str = zend_string_init(ZSTR_VAL(regex), ZSTR_LEN(regex), 1); - GC_REFCOUNT(str) = 0; /* will be incremented by zend_hash_update_mem() */ - ZSTR_H(str) = ZSTR_H(regex); - regex = str; + if (!ZSTR_IS_INTERNED(key) || !(GC_FLAGS(key) & IS_STR_PERMANENT)) { + pce = zend_hash_str_update_mem(&PCRE_G(pcre_cache), + ZSTR_VAL(key), ZSTR_LEN(key), &new_entry, sizeof(pcre_cache_entry)); +#if HAVE_SETLOCALE + if (key != regex) { + zend_string_release(key); + } +#endif + } else { + pce = zend_hash_update_mem(&PCRE_G(pcre_cache), key, &new_entry, sizeof(pcre_cache_entry)); } - pce = zend_hash_update_mem(&PCRE_G(pcre_cache), regex, &new_entry, sizeof(pcre_cache_entry)); - return pce; } /* }}} */ diff --git a/ext/pcre/tests/bug73483.phpt b/ext/pcre/tests/bug73483.phpt new file mode 100644 index 0000000000..fd10702527 --- /dev/null +++ b/ext/pcre/tests/bug73483.phpt @@ -0,0 +1,15 @@ +--TEST-- +Bug #73483 (Segmentation fault on pcre_replace_callback) +--FILE-- +<?php +$regex = "#dummy#"; +setlocale(LC_ALL, "C"); +var_dump(preg_replace_callback($regex, function (array $matches) use($regex) { + setlocale(LC_ALL, "en_US"); + $ret = preg_replace($regex, "okey", $matches[0]); + setlocale(LC_ALL, "C"); + return $ret; +}, "dummy")); +?> +--EXPECT-- +string(4) "okey" |