8000 Fix #81243: Too much memory is allocated for preg_replace() · nikic/php-src@a6b4308 · GitHub
[go: up one dir, main page]

Skip to content

Commit a6b4308

Browse files
committed
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 phpGH-7231.
1 parent bb43aa2 commit a6b4308

File tree

3 files changed

+36
-18
lines changed

3 files changed

+36
-18
lines changed

NEWS

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ PHP NEWS
2525

2626
- PCRE:
2727
. Fixed bug #81101 (PCRE2 10.37 shows unexpected result). (Anatol)
28+
. Fixed bug #81243 (Too much memory is allocated for preg_replace()). (cmb)
2829

2930
- Standard:
3031
. Fixed bug #81223 (flock() only locks first byte of file). (cmb)

ext/pcre/php_pcre.c

Lines changed: 14 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1719,7 +1719,7 @@ PHPAPI zend_string *php_pcre_replace_impl(pcre_cache_entry *pce, zend_string *su
17191719
}
17201720

17211721
if (new_len >= alloc_len) {
1722-
alloc_len = zend_safe_address_guarded(2, new_len, alloc_len);
1722+
alloc_len = zend_safe_address_guarded(2, new_len, 0);
17231723
if (result == NULL) {
17241724
result = zend_string_alloc(alloc_len, 0);
17251725
} else {
@@ -1805,14 +1805,12 @@ PHPAPI zend_string *php_pcre_replace_impl(pcre_cache_entry *pce, zend_string *su
18051805
result = zend_string_copy(subject_str);
18061806
break;
18071807
}
1808-
new_len = result_len + subject_len - last_end_offset;
1809-
if (new_len >= alloc_len) {
1810-
alloc_len = new_len; /* now we know exactly how long it is */
1811-
if (NULL != result) {
1812-
result = zend_string_realloc(result, alloc_len, 0);
1813-
} else {
1814-
result = zend_string_alloc(alloc_len, 0);
1815-
}
1808+
/* now we know exactly how long it is */
1809+
alloc_len = result_len + subject_len - last_end_offset;
1810+
if (NULL != result) {
1811+
result = zend_string_realloc(result, alloc_len, 0);
1812+
} else {
1813+
result = zend_string_alloc(alloc_len, 0);
18161814
}
18171815
/* stick that last bit of string on our output */
18181816
memcpy(ZSTR_VAL(result) + result_len, piece, subject_len - last_end_offset);
@@ -1959,7 +1957,7 @@ static zend_string *php_pcre_replace_func_impl(pcre_cache_entry *pce, zend_strin
19591957
ZEND_ASSERT(eval_result);
19601958
new_len = zend_safe_address_guarded(1, ZSTR_LEN(eval_result), new_len);
19611959
if (new_len >= alloc_len) {
1962-
alloc_len = zend_safe_address_guarded(2, new_len, alloc_len);
1960+
alloc_len = zend_safe_address_guarded(2, new_len, 0);
19631961
if (result == NULL) {
19641962
result = zend_string_alloc(alloc_len, 0);
19651963
} else {
@@ -2016,14 +2014,12 @@ static zend_string *php_pcre_replace_func_impl(pcre_cache_entry *pce, zend_strin
20162014
result = zend_string_copy(subject_str);
20172015
break;
20182016
}
2019-
new_len = result_len + subject_len - last_end_offset;
2020-
if (new_len >= alloc_len) {
2021-
alloc_len = new_len; /* now we know exactly how long it is */
2022-
if (NULL != result) {
2023-
result = zend_string_realloc(result, alloc_len, 0);
2024-
} else {
2025-
result = zend_string_alloc(alloc_len, 0);
2026-
}
2017+
/* now we know exactly how long it is */
2018+
alloc_len = result_len + subject_len - last_end_offset;
2019+
if (NULL != result) {
2020+
result = zend_string_realloc(result, alloc_len, 0);
2021+
} else {
2022+
result = zend_string_alloc(alloc_len, 0);
20272023
}
20282024
/* stick that last bit of string on our output */
20292025
memcpy(ZSTR_VAL(result) + result_len, piece, subject_len - last_end_offset);

ext/pcre/tests/bug81243.phpt

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
--TEST--
2+
Bug #81243 (Too much memory is allocated for preg_replace())
3+
--FILE--
4+
<?php
5+
$test_string = str_repeat('Eins zwei drei', 2000);
6+
7+
$replaced = preg_replace('/\s/', '-', $test_string);
8+
$mem0 = memory_get_usage();
9+
$replaced = str_repeat($replaced, 1);
10+
$mem1 = memory_get_usage();
11+
var_dump($mem0 == $mem1);
12+
13+
$replaced = preg_replace_callback('/\s/', function ($_) {return '-';}, $test_string);
14+
$mem0 = memory_get_usage();
15+
$replaced = str_repeat($replaced, 1);
16+
$mem1 = memory_get_usage();
17+
var_dump($mem0 == $mem1);
18+
?>
19+
--EXPECT--
20+
bool(true)
21+
bool(true)

0 commit comments

Comments
 (0)
0