@@ -1954,9 +1954,21 @@ ZEND_API zend_result ZEND_FASTCALL shift_right_function(zval *result, zval *op1,
1954
1954
ZEND_API zend_result ZEND_FASTCALL concat_function (zval * result , zval * op1 , zval * op2 ) /* {{{ */
1955
1955
{
1956
1956
zval * orig_op1 = op1 ;
1957
+ zval orig_result = {0 };
1957
1958
zend_string * op1_string , * op2_string ;
1958
1959
bool free_op1_string = false;
1959
1960
bool free_op2_string = false;
1961
+ bool result_is_orig = (result == orig_op1 );
1962
+
1963
+ /* Save the original result value and protect it from being freed during conversion.
1964
+ * We add a reference because destructors can modify the zval in-place, and we need
1965
+ * to ensure we can properly clean up the original value. */
1966
+ if (result_is_orig ) {
1967
+ ZVAL_COPY_VALUE (& orig_result , result );
1968
+ if (Z_REFCOUNTED (orig_result )) {
1969
+ Z_ADDREF (orig_result );
1970
+ }
1971
+ }
1960
1972
1961
1973
do {
1962
1974
if (EXPECTED (Z_TYPE_P (op1 ) == IS_STRING )) {
@@ -1973,6 +1985,9 @@ ZEND_API zend_result ZEND_FASTCALL concat_function(zval *result, zval *op1, zval
1973
1985
op1_string = zval_get_string_func (op1 );
1974
1986
if (UNEXPECTED (EG (exception ))) {
1975
1987
zend_string_release (op1_string );
1988
+ if (result_is_orig && Z_REFCOUNTED (orig_result )) {
1989
+ i_zval_ptr_dtor (& orig_result );
1990
+ }
1976
1991
if (orig_op1 != result ) {
1977
1992
ZVAL_UNDEF (result );
1978
1993
}
@@ -2008,6 +2023,9 @@ ZEND_API zend_result ZEND_FASTCALL concat_function(zval *result, zval *op1, zval
2008
2023
if (UNEXPECTED (EG (exception ))) {
2009
2024
zend_string_release (op1_string );
2010
2025
zend_string_release (op2_string );
2026
+ if (result_is_orig && Z_REFCOUNTED (orig_result )) {
2027
+ i_zval_ptr_dtor (& orig_result );
2028
+ }
2011
2029
if (orig_op1 != result ) {
2012
2030
ZVAL_UNDEF (result );
2013
2031
}
@@ -2020,9 +2038,6 @@ ZEND_API zend_result ZEND_FASTCALL concat_function(zval *result, zval *op1, zval
2020
2038
has_op2_string :;
2021
2039
if (UNEXPECTED (ZSTR_LEN (op1_string ) == 0 )) {
2022
2040
if (EXPECTED (result != op2 || Z_TYPE_P (result ) != IS_STRING )) {
2023
- if (result == orig_op1 ) {
2024
- i_zval_ptr_dtor (result );
2025
- }
2026
2041
if (free_op2_string ) {
2027
2042
/* transfer ownership of op2_string */
2028
2043
ZVAL_STR (result , op2_string );
@@ -2033,9 +2048,6 @@ has_op2_string:;
2033
2048
}
2034
2049
} else if (UNEXPECTED (ZSTR_LEN (op2_string ) == 0 )) {
2035
2050
if (EXPECTED (result != op1 || Z_TYPE_P (result ) != IS_STRING )) {
2036
- if (result == orig_op1 ) {
2037
- i_zval_ptr_dtor (result );
2038
- }
2039
2051
if (free_op1_string ) {
2040
2052
/* transfer ownership of op1_string */
2041
2053
ZVAL_STR (result , op1_string );
@@ -2054,6 +2066,9 @@ has_op2_string:;
2054
2066
if (UNEXPECTED (op1_len > ZSTR_MAX_LEN - op2_len )) {
2055
2067
if (free_op1_string ) zend_string_release (op1_string );
2056
2068
if (free_op2_string ) zend_string_release (op2_string );
2069
+ if (result_is_orig && Z_REFCOUNTED (orig_result )) {
2070
+ i_zval_ptr_dtor (& orig_result );
2071
+ }
2057
2072
zend_throw_error (NULL , "String size overflow" );
2058
2073
if (orig_op1 != result ) {
2059
2074
ZVAL_UNDEF (result );
@@ -2065,7 +2080,9 @@ has_op2_string:;
2065
2080
/* Destroy the old result first to drop the refcount, such that $x .= ...; may happen in-place. */
2066
2081
if (free_op1_string ) {
2067
2082
/* op1_string will be used as the result, so we should not free it */
2068
- i_zval_ptr_dtor (result );
2083
+ if (!result_is_orig ) {
2084
+ i_zval_ptr_dtor (result );
2085
+ }
2069
2086
/* Set it to NULL in case that the extension will throw an out-of-memory error.
2070
2087
* Otherwise the shutdown sequence will try to free this again. */
2071
2088
ZVAL_NULL (result );
@@ -2095,6 +2112,10 @@ has_op2_string:;
2095
2112
ZSTR_VAL (result_str )[result_len ] = '\0' ;
2096
2113
}
2097
2114
2115
+ if (result_is_orig && Z_REFCOUNTED (orig_result )) {
2116
+ i_zval_ptr_dtor (& orig_result );
2117
+ }
2118
+
2098
2119
if (free_op1_string ) zend_string_release (op1_string );
2099
2120
if (free_op2_string ) zend_string_release (op2_string );
2100
2121
0 commit comments