Skip to content

Commit e30f52b

Browse files
committed
Merge branch 'PHP-7.3' into PHP-7.4
* PHP-7.3: Fixed bug #79188
2 parents d9f51e0 + 13bfa9f commit e30f52b

File tree

3 files changed

+36
-18
lines changed

3 files changed

+36
-18
lines changed

NEWS

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,12 @@
11
PHP NEWS
22
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
3+
4+
?? ??? ????, PHP 7.4.4
5+
6+
- PCRE:
7+
. Fixed bug #79188 (Memory corruption in preg_replace/preg_replace_callback
8+
and unicode). (Nikita)
9+
310
?? ??? ????, PHP 7.4.3
411

512
- Core:

ext/pcre/php_pcre.c

Lines changed: 16 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1582,6 +1582,7 @@ PHPAPI zend_string *php_pcre_replace_impl(pcre_cache_entry *pce, zend_string *su
15821582
size_t match_len; /* Length of the current match */
15831583
int backref; /* Backreference number */
15841584
PCRE2_SIZE start_offset; /* Where the new search starts */
1585+
size_t last_end_offset; /* Where the last search ended */
15851586
char *walkbuf, /* Location of current replacement in the result */
15861587
*walk, /* Used to walk the replacement string */
15871588
*match, /* The current match */
@@ -1600,6 +1601,7 @@ PHPAPI zend_string *php_pcre_replace_impl(pcre_cache_entry *pce, zend_string *su
16001601
/* Initialize */
16011602
match = NULL;
16021603
start_offset = 0;
1604+
last_end_offset = 0;
16031605
result_len = 0;
16041606
PCRE_G(error_code) = PHP_PCRE_NO_ERROR;
16051607

@@ -1626,7 +1628,7 @@ PHPAPI zend_string *php_pcre_replace_impl(pcre_cache_entry *pce, zend_string *su
16261628
options, match_data, mctx);
16271629

16281630
while (1) {
1629-
piece = subject + start_offset;
1631+
piece = subject + last_end_offset;
16301632

16311633
if (count >= 0 && limit > 0) {
16321634
zend_bool simple_string;
@@ -1656,7 +1658,7 @@ PHPAPI zend_string *php_pcre_replace_impl(pcre_cache_entry *pce, zend_string *su
16561658
/* Set the match location in subject */
16571659
match = subject + offsets[0];
16581660

1659-
new_len = result_len + offsets[0] - start_offset; /* part before the match */
1661+
new_len = result_len + offsets[0] - last_end_offset; /* part before the match */
16601662

16611663
walk = ZSTR_VAL(replace_str);
16621664
replace_end = walk + ZSTR_LEN(replace_str);
@@ -1733,7 +1735,7 @@ PHPAPI zend_string *php_pcre_replace_impl(pcre_cache_entry *pce, zend_string *su
17331735
limit--;
17341736

17351737
/* Advance to the next piece. */
1736-
start_offset = offsets[1];
1738+
start_offset = last_end_offset = offsets[1];
17371739

17381740
/* If we have matched an empty string, mimic what Perl's /g options does.
17391741
This turns out to be rather cunning. First we set PCRE2_NOTEMPTY_ATSTART and try
@@ -1753,10 +1755,7 @@ PHPAPI zend_string *php_pcre_replace_impl(pcre_cache_entry *pce, zend_string *su
17531755
to achieve this, unless we're already at the end of the string. */
17541756
if (start_offset < subject_len) {
17551757
size_t unit_len = calculate_unit_length(pce, piece);
1756-
17571758
start_offset += unit_len;
1758-
memcpy(ZSTR_VAL(result) + result_len, piece, unit_len);
1759-
result_len += unit_len;
17601759
} else {
17611760
goto not_matched;
17621761
}
@@ -1771,7 +1770,7 @@ PHPAPI zend_string *php_pcre_replace_impl(pcre_cache_entry *pce, zend_string *su
17711770
result = zend_string_copy(subject_str);
17721771
break;
17731772
}
1774-
new_len = result_len + subject_len - start_offset;
1773+
new_len = result_len + subject_len - last_end_offset;
17751774
if (new_len >= alloc_len) {
17761775
alloc_len = new_len; /* now we know exactly how long it is */
17771776
if (NULL != result) {
@@ -1781,8 +1780,8 @@ PHPAPI zend_string *php_pcre_replace_impl(pcre_cache_entry *pce, zend_string *su
17811780
}
17821781
}
17831782
/* stick that last bit of string on our output */
1784-
memcpy(ZSTR_VAL(result) + result_len, piece, subject_len - start_offset);
1785-
result_len += subject_len - start_offset;
1783+
memcpy(ZSTR_VAL(result) + result_len, piece, subject_len - last_end_offset);
1784+
result_len += subject_len - last_end_offset;
17861785
ZSTR_VAL(result)[result_len] = '\0';
17871786
ZSTR_LEN(result) = result_len;
17881787
break;
@@ -1824,6 +1823,7 @@ static zend_string *php_pcre_replace_func_impl(pcre_cache_entry *pce, zend_strin
18241823
size_t new_len; /* Length of needed storage */
18251824
size_t alloc_len; /* Actual allocated length */
18261825
PCRE2_SIZE start_offset; /* Where the new search starts */
1826+
size_t last_end_offset; /* Where the last search ended */
18271827
char *match, /* The current match */
18281828
*piece; /* The current piece of subject */
18291829
size_t result_len; /* Length of result */
@@ -1853,6 +1853,7 @@ static zend_string *php_pcre_replace_func_impl(pcre_cache_entry *pce, zend_strin
18531853
/* Initialize */
18541854
match = NULL;
18551855
start_offset = 0;
1856+
last_end_offset = 0;
18561857
result_len = 0;
18571858
PCRE_G(error_code) = PHP_PCRE_NO_ERROR;
18581859

@@ -1885,7 +1886,7 @@ static zend_string *php_pcre_replace_func_impl(pcre_cache_entry *pce, zend_strin
18851886
options, match_data, mctx);
18861887

18871888
while (1) {
1888-
piece = subject + start_offset;
1889+
piece = subject + last_end_offset;
18891890

18901891
if (count >= 0 && limit) {
18911892
/* Check for too many substrings condition. */
@@ -1913,7 +1914,7 @@ static zend_string *php_pcre_replace_func_impl(pcre_cache_entry *pce, zend_strin
19131914
/* Set the match location in subject */
19141915
match = subject + offsets[0];
19151916

1916-
new_len = result_len + offsets[0] - start_offset; /* part before the match */
1917+
new_len = result_len + offsets[0] - last_end_offset; /* part before the match */
19171918

19181919
/* Use custom function to get replacement string and its length. */
19191920
eval_result = preg_do_repl_func(
@@ -1945,7 +1946,7 @@ static zend_string *php_pcre_replace_func_impl(pcre_cache_entry *pce, zend_strin
19451946
limit--;
19461947

19471948
/* Advance to the next piece. */
1948-
start_offset = offsets[1];
1949+
start_offset = last_end_offset = offsets[1];
19491950

19501951
/* If we have matched an empty string, mimic what Perl's /g options does.
19511952
This turns out to be rather cunning. First we set PCRE2_NOTEMPTY_ATSTART and try
@@ -1965,10 +1966,7 @@ static zend_string *php_pcre_replace_func_impl(pcre_cache_entry *pce, zend_strin
19651966
to achieve this, unless we're already at the end of the string. */
19661967
if (start_offset < subject_len) {
19671968
size_t unit_len = calculate_unit_length(pce, piece);
1968-
19691969
start_offset += unit_len;
1970-
memcpy(ZSTR_VAL(result) + result_len, piece, unit_len);
1971-
result_len += unit_len;
19721970
} else {
19731971
goto not_matched;
19741972
}
@@ -1983,7 +1981,7 @@ static zend_string *php_pcre_replace_func_impl(pcre_cache_entry *pce, zend_strin
19831981
result = zend_string_copy(subject_str);
19841982
break;
19851983
}
1986-
new_len = result_len + subject_len - start_offset;
1984+
new_len = result_len + subject_len - last_end_offset;
19871985
if (new_len >= alloc_len) {
19881986
alloc_len = new_len; /* now we know exactly how long it is */
19891987
if (NULL != result) {
@@ -1993,8 +1991,8 @@ static zend_string *php_pcre_replace_func_impl(pcre_cache_entry *pce, zend_strin
19931991
}
19941992
}
19951993
/* stick that last bit of string on our output */
1996-
memcpy(ZSTR_VAL(result) + result_len, piece, subject_len - start_offset);
1997-
result_len += subject_len - start_offset;
1994+
memcpy(ZSTR_VAL(result) + result_len, piece, subject_len - last_end_offset);
1995+
result_len += subject_len - last_end_offset;
19981996
ZSTR_VAL(result)[result_len] = '\0';
19991997
ZSTR_LEN(result) = result_len;
20001998
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)