Skip to content

Commit 3c87266

Browse files
committed
Fix GH-11507: String concatenation performance regression in 8.3
When the code was moved to solve the uaf for memory overflow, this caused the refcount to be higher than one in some self-concatenation scenarios. This in turn causes quadratic time performance problems when these concatenations happen in a loop. Closes GH-11508.
1 parent 33e2868 commit 3c87266

File tree

2 files changed

+10
-5
lines changed

2 files changed

+10
-5
lines changed

NEWS

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,10 @@ PHP NEWS
22
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
33
?? ??? ????, PHP 8.3.0alpha3
44

5+
- Core:
6+
. Fixed bug GH-11507 (String concatenation performance regression in 8.3).
7+
(nielsdos)
8+
59
- MBString:
610
. Implement mb_str_pad() RFC. (nielsdos)
711

Zend/zend_operators.c

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2057,16 +2057,17 @@ has_op2_string:;
20572057
}
20582058

20592059
if (result == op1) {
2060-
/* special case, perform operations on result */
2061-
result_str = zend_string_extend(op1_string, result_len, 0);
2062-
/* Free result after zend_string_extend(), as it may throw an out-of-memory error. If we
2063-
* free it before we would leave the released variable on the stack with shutdown trying
2064-
* to free it again. */
2060+
/* Destroy the old result first to drop the refcount, such that $x .= ...; may happen in-place. */
20652061
if (free_op1_string) {
20662062
/* op1_string will be used as the result, so we should not free it */
20672063
i_zval_ptr_dtor(result);
2064+
/* Set it to NULL in case that the extension will throw an out-of-memory error.
2065+
* Otherwise the shutdown sequence will try to free this again. */
2066+
ZVAL_NULL(result);
20682067
free_op1_string = false;
20692068
}
2069+
/* special case, perform operations on result */
2070+
result_str = zend_string_extend(op1_string, result_len, 0);
20702071
/* account for the case where result_str == op1_string == op2_string and the realloc is done */
20712072
if (op1_string == op2_string) {
20722073
if (free_op2_string) {

0 commit comments

Comments
 (0)