Skip to content

Commit 0652241

Browse files
committed
Merge branch 'PHP-7.4'
* PHP-7.4: Fixed bug #79188
2 parents b3f17ea + e30f52b commit 0652241

File tree

2 files changed

+29
-18
lines changed

2 files changed

+29
-18
lines changed

ext/pcre/php_pcre.c

Lines changed: 16 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1561,6 +1561,7 @@ PHPAPI zend_string *php_pcre_replace_impl(pcre_cache_entry *pce, zend_string *su
15611561
size_t match_len; /* Length of the current match */
15621562
int backref; /* Backreference number */
15631563
PCRE2_SIZE start_offset; /* Where the new search starts */
1564+
size_t last_end_offset; /* Where the last search ended */
15641565
char *walkbuf, /* Location of current replacement in the result */
15651566
*walk, /* Used to walk the replacement string */
15661567
*match, /* The current match */
@@ -1579,6 +1580,7 @@ PHPAPI zend_string *php_pcre_replace_impl(pcre_cache_entry *pce, zend_string *su
15791580
/* Initialize */
15801581
match = NULL;
15811582
start_offset = 0;
1583+
last_end_offset = 0;
15821584
result_len = 0;
15831585
PCRE_G(error_code) = PHP_PCRE_NO_ERROR;
15841586

@@ -1605,7 +1607,7 @@ PHPAPI zend_string *php_pcre_replace_impl(pcre_cache_entry *pce, zend_string *su
16051607
options, match_data, mctx);
16061608

16071609
while (1) {
1608-
piece = subject + start_offset;
1610+
piece = subject + last_end_offset;
16091611

16101612
if (count >= 0 && limit > 0) {
16111613
zend_bool simple_string;
@@ -1635,7 +1637,7 @@ PHPAPI zend_string *php_pcre_replace_impl(pcre_cache_entry *pce, zend_string *su
16351637
/* Set the match location in subject */
16361638
match = subject + offsets[0];
16371639

1638-
new_len = result_len + offsets[0] - start_offset; /* part before the match */
1640+
new_len = result_len + offsets[0] - last_end_offset; /* part before the match */
16391641

16401642
walk = ZSTR_VAL(replace_str);
16411643
replace_end = walk + ZSTR_LEN(replace_str);
@@ -1712,7 +1714,7 @@ PHPAPI zend_string *php_pcre_replace_impl(pcre_cache_entry *pce, zend_string *su
17121714
limit--;
17131715

17141716
/* Advance to the next piece. */
1715-
start_offset = offsets[1];
1717+
start_offset = last_end_offset = offsets[1];
17161718

17171719
/* If we have matched an empty string, mimic what Perl's /g options does.
17181720
This turns out to be rather cunning. First we set PCRE2_NOTEMPTY_ATSTART and try
@@ -1732,10 +1734,7 @@ PHPAPI zend_string *php_pcre_replace_impl(pcre_cache_entry *pce, zend_string *su
17321734
to achieve this, unless we're already at the end of the string. */
17331735
if (start_offset < subject_len) {
17341736
size_t unit_len = calculate_unit_length(pce, piece);
1735-
17361737
start_offset += unit_len;
1737-
memcpy(ZSTR_VAL(result) + result_len, piece, unit_len);
1738-
result_len += unit_len;
17391738
} else {
17401739
goto not_matched;
17411740
}
@@ -1750,7 +1749,7 @@ PHPAPI zend_string *php_pcre_replace_impl(pcre_cache_entry *pce, zend_string *su
17501749
result = zend_string_copy(subject_str);
17511750
break;
17521751
}
1753-
new_len = result_len + subject_len - start_offset;
1752+
new_len = result_len + subject_len - last_end_offset;
17541753
if (new_len >= alloc_len) {
17551754
alloc_len = new_len; /* now we know exactly how long it is */
17561755
if (NULL != result) {
@@ -1760,8 +1759,8 @@ PHPAPI zend_string *php_pcre_replace_impl(pcre_cache_entry *pce, zend_string *su
17601759
}
17611760
}
17621761
/* stick that last bit of string on our output */
1763-
memcpy(ZSTR_VAL(result) + result_len, piece, subject_len - start_offset);
1764-
result_len += subject_len - start_offset;
1762+
memcpy(ZSTR_VAL(result) + result_len, piece, subject_len - last_end_offset);
1763+
result_len += subject_len - last_end_offset;
17651764
ZSTR_VAL(result)[result_len] = '\0';
17661765
ZSTR_LEN(result) = result_len;
17671766
break;
@@ -1803,6 +1802,7 @@ static zend_string *php_pcre_replace_func_impl(pcre_cache_entry *pce, zend_strin
18031802
size_t new_len; /* Length of needed storage */
18041803
size_t alloc_len; /* Actual allocated length */
18051804
PCRE2_SIZE start_offset; /* Where the new search starts */
1805+
size_t last_end_offset; /* Where the last search ended */
18061806
char *match, /* The current match */
18071807
*piece; /* The current piece of subject */
18081808
size_t result_len; /* Length of result */
@@ -1832,6 +1832,7 @@ static zend_string *php_pcre_replace_func_impl(pcre_cache_entry *pce, zend_strin
18321832
/* Initialize */
18331833
match = NULL;
18341834
start_offset = 0;
1835+
last_end_offset = 0;
18351836
result_len = 0;
18361837
PCRE_G(error_code) = PHP_PCRE_NO_ERROR;
18371838

@@ -1864,7 +1865,7 @@ static zend_string *php_pcre_replace_func_impl(pcre_cache_entry *pce, zend_strin
18641865
options, match_data, mctx);
18651866

18661867
while (1) {
1867-
piece = subject + start_offset;
1868+
piece = subject + last_end_offset;
18681869

18691870
if (count >= 0 && limit) {
18701871
/* Check for too many substrings condition. */
@@ -1892,7 +1893,7 @@ static zend_string *php_pcre_replace_func_impl(pcre_cache_entry *pce, zend_strin
18921893
/* Set the match location in subject */
18931894
match = subject + offsets[0];
18941895

1895-
new_len = result_len + offsets[0] - start_offset; /* part before the match */
1896+
new_len = result_len + offsets[0] - last_end_offset; /* part before the match */
18961897

18971898
/* Use custom function to get replacement string and its length. */
18981899
eval_result = preg_do_repl_func(
@@ -1924,7 +1925,7 @@ static zend_string *php_pcre_replace_func_impl(pcre_cache_entry *pce, zend_strin
19241925
limit--;
19251926

19261927
/* Advance to the next piece. */
1927-
start_offset = offsets[1];
1928+
start_offset = last_end_offset = offsets[1];
19281929

19291930
/* If we have matched an empty string, mimic what Perl's /g options does.
19301931
This turns out to be rather cunning. First we set PCRE2_NOTEMPTY_ATSTART and try
@@ -1944,10 +1945,7 @@ static zend_string *php_pcre_replace_func_impl(pcre_cache_entry *pce, zend_strin
19441945
to achieve this, unless we're already at the end of the string. */
19451946
if (start_offset < subject_len) {
19461947
size_t unit_len = calculate_unit_length(pce, piece);
1947-
19481948
start_offset += unit_len;
1949-
memcpy(ZSTR_VAL(result) + result_len, piece, unit_len);
1950-
result_len += unit_len;
19511949
} else {
19521950
goto not_matched;
19531951
}
@@ -1962,7 +1960,7 @@ static zend_string *php_pcre_replace_func_impl(pcre_cache_entry *pce, zend_strin
19621960
result = zend_string_copy(subject_str);
19631961
break;
19641962
}
1965-
new_len = result_len + subject_len - start_offset;
1963+
new_len = result_len + subject_len - last_end_offset;
19661964
if (new_len >= alloc_len) {
19671965
alloc_len = new_len; /* now we know exactly how long it is */
19681966
if (NULL != result) {
@@ -1972,8 +1970,8 @@ static zend_string *php_pcre_replace_func_impl(pcre_cache_entry *pce, zend_strin
19721970
}
19731971
}
19741972
/* stick that last bit of string on our output */
1975-
memcpy(ZSTR_VAL(result) + result_len, piece, subject_len - start_offset);
1976-
result_len += subject_len - start_offset;
1973+
memcpy(ZSTR_VAL(result) + result_len, piece, subject_len - last_end_offset);
1974+
result_len += subject_len - last_end_offset;
19771975
ZSTR_VAL(result)[result_len] = '\0';
19781976
ZSTR_LEN(result) = result_len;
19791977
break;

ext/pcre/tests/bug79188.phpt

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
--TEST--
2+
Bug #79188: Memory corruption in preg_replace/preg_replace_callback and unicode
3+
--FILE--
4+
<?php
5+
6+
var_dump(preg_replace("//u", "", "a" . str_repeat("\u{1f612}", 10)));
7+
var_dump(preg_replace_callback(
8+
"//u", function() { return ""; }, "a" . str_repeat("\u{1f612}", 10)));
9+
10+
?>
11+
--EXPECT--
12+
string(41) "a😒😒😒😒😒😒😒😒😒😒"
13+
string(41) "a😒😒😒😒😒😒😒😒😒😒"

0 commit comments

Comments
 (0)