From a6b43086e6799eedf02d36ccea103e2b10c1005a Mon Sep 17 00:00:00 2001 From: "Christoph M. Becker" Date: Mon, 12 Jul 2021 16:35:35 +0200 Subject: [PATCH] Fix #81243: Too much memory is allocated for preg_replace() Trimming a potentially over-allocated string appears to be reasonable, so we drop the condition altogether. We also re-allocate twice the size needed in the first place, and not roughly tripple the size. Closes GH-7231. --- NEWS | 1 + ext/pcre/php_pcre.c | 32 ++++++++++++++------------------ ext/pcre/tests/bug81243.phpt | 21 +++++++++++++++++++++ 3 files changed, 36 insertions(+), 18 deletions(-) create mode 100644 ext/pcre/tests/bug81243.phpt diff --git a/NEWS b/NEWS index 527d999a051..6b93d651d10 100644 --- a/NEWS +++ b/NEWS @@ -25,6 +25,7 @@ PHP NEWS - PCRE: . Fixed bug #81101 (PCRE2 10.37 shows unexpected result). (Anatol) + . Fixed bug #81243 (Too much memory is allocated for preg_replace()). (cmb) - Standard: . Fixed bug #81223 (flock() only locks first byte of file). (cmb) diff --git a/ext/pcre/php_pcre.c b/ext/pcre/php_pcre.c index 64da43a05f8..19ea9271387 100644 --- a/ext/pcre/php_pcre.c +++ b/ext/pcre/php_pcre.c @@ -1719,7 +1719,7 @@ matched: } if (new_len >= alloc_len) { - alloc_len = zend_safe_address_guarded(2, new_len, alloc_len); + alloc_len = zend_safe_address_guarded(2, new_len, 0); if (result == NULL) { result = zend_string_alloc(alloc_len, 0); } else { @@ -1805,14 +1805,12 @@ not_matched: result = zend_string_copy(subject_str); break; } - new_len = result_len + subject_len - last_end_offset; - if (new_len >= alloc_len) { - alloc_len = new_len; /* now we know exactly how long it is */ - if (NULL != result) { - result = zend_string_realloc(result, alloc_len, 0); - } else { - result = zend_string_alloc(alloc_len, 0); - } + /* now we know exactly how long it is */ + alloc_len = result_len + subject_len - last_end_offset; + if (NULL != result) { + result = zend_string_realloc(result, alloc_len, 0); + } else { + result = zend_string_alloc(alloc_len, 0); } /* stick that last bit of string on our output */ memcpy(ZSTR_VAL(result) + result_len, piece, subject_len - last_end_offset); @@ -1959,7 +1957,7 @@ matched: ZEND_ASSERT(eval_result); new_len = zend_safe_address_guarded(1, ZSTR_LEN(eval_result), new_len); if (new_len >= alloc_len) { - alloc_len = zend_safe_address_guarded(2, new_len, alloc_len); + alloc_len = zend_safe_address_guarded(2, new_len, 0); if (result == NULL) { result = zend_string_alloc(alloc_len, 0); } else { @@ -2016,14 +2014,12 @@ not_matched: result = zend_string_copy(subject_str); break; } - new_len = result_len + subject_len - last_end_offset; - if (new_len >= alloc_len) { - alloc_len = new_len; /* now we know exactly how long it is */ - if (NULL != result) { - result = zend_string_realloc(result, alloc_len, 0); - } else { - result = zend_string_alloc(alloc_len, 0); - } + /* now we know exactly how long it is */ + alloc_len = result_len + subject_len - last_end_offset; + if (NULL != result) { + result = zend_string_realloc(result, alloc_len, 0); + } else { + result = zend_string_alloc(alloc_len, 0); } /* stick that last bit of string on our output */ memcpy(ZSTR_VAL(result) + result_len, piece, subject_len - last_end_offset); diff --git a/ext/pcre/tests/bug81243.phpt b/ext/pcre/tests/bug81243.phpt new file mode 100644 index 00000000000..a413f33f6ee --- /dev/null +++ b/ext/pcre/tests/bug81243.phpt @@ -0,0 +1,21 @@ +--TEST-- +Bug #81243 (Too much memory is allocated for preg_replace()) +--FILE-- + +--EXPECT-- +bool(true) +bool(true)