@@ -1940,108 +1940,146 @@ ZEND_API zend_result ZEND_FASTCALL shift_right_function(zval *result, zval *op1,
19401940ZEND_API zend_result ZEND_FASTCALL concat_function (zval * result , zval * op1 , zval * op2 ) /* {{{ */
19411941{
19421942 zval * orig_op1 = op1 ;
1943- zval op1_copy , op2_copy ;
1944-
1945- ZVAL_UNDEF (& op1_copy );
1946- ZVAL_UNDEF (& op2_copy );
1943+ zend_string * op1_string , * op2_string ;
1944+ bool free_op1_string = false;
1945+ bool free_op2_string = false;
19471946
19481947 do {
1949- if (UNEXPECTED (Z_TYPE_P (op1 ) != IS_STRING )) {
1948+ if (EXPECTED (Z_TYPE_P (op1 ) == IS_STRING )) {
1949+ op1_string = Z_STR_P (op1 );
1950+ } else {
19501951 if (Z_ISREF_P (op1 )) {
19511952 op1 = Z_REFVAL_P (op1 );
1952- if (Z_TYPE_P (op1 ) == IS_STRING ) break ;
1953+ if (Z_TYPE_P (op1 ) == IS_STRING ) {
1954+ op1_string = Z_STR_P (op1 );
1955+ break ;
1956+ }
19531957 }
19541958 ZEND_TRY_BINARY_OBJECT_OPERATION (ZEND_CONCAT );
1955- ZVAL_STR ( & op1_copy , zval_get_string_func (op1 ) );
1959+ op1_string = zval_get_string_func (op1 );
19561960 if (UNEXPECTED (EG (exception ))) {
1957- zval_ptr_dtor_str ( & op1_copy );
1961+ zend_string_release ( op1_string );
19581962 if (orig_op1 != result ) {
19591963 ZVAL_UNDEF (result );
19601964 }
19611965 return FAILURE ;
19621966 }
1967+ free_op1_string = true;
19631968 if (result == op1 ) {
19641969 if (UNEXPECTED (op1 == op2 )) {
1965- op2 = & op1_copy ;
1970+ op2_string = op1_string ;
1971+ goto has_op2_string ;
19661972 }
19671973 }
1968- op1 = & op1_copy ;
19691974 }
19701975 } while (0 );
19711976 do {
1972- if (UNEXPECTED (Z_TYPE_P (op2 ) != IS_STRING )) {
1973- if (Z_ISREF_P (op2 )) {
1974- op2 = Z_REFVAL_P (op2 );
1975- if (Z_TYPE_P (op2 ) == IS_STRING ) break ;
1976- }
1977+ if (EXPECTED (Z_TYPE_P (op2 ) == IS_STRING )) {
1978+ op2_string = Z_STR_P (op2 );
1979+ } else {
1980+ if (Z_ISREF_P (op2 )) {
1981+ op2 = Z_REFVAL_P (op2 );
1982+ if (Z_TYPE_P (op2 ) == IS_STRING ) {
1983+ op2_string = Z_STR_P (op2 );
1984+ break ;
1985+ }
1986+ }
1987+ /* hold an additional reference because a userland function could free this */
1988+ if (!free_op1_string ) {
1989+ op1_string = zend_string_copy (op1_string );
1990+ free_op1_string = true;
1991+ }
19771992 ZEND_TRY_BINARY_OP2_OBJECT_OPERATION (ZEND_CONCAT );
1978- ZVAL_STR ( & op2_copy , zval_get_string_func (op2 ) );
1993+ op2_string = zval_get_string_func (op2 );
19791994 if (UNEXPECTED (EG (exception ))) {
1980- zval_ptr_dtor_str ( & op1_copy );
1981- zval_ptr_dtor_str ( & op2_copy );
1995+ zend_string_release ( op1_string );
1996+ zend_string_release ( op2_string );
19821997 if (orig_op1 != result ) {
19831998 ZVAL_UNDEF (result );
19841999 }
19852000 return FAILURE ;
19862001 }
1987- op2 = & op2_copy ;
2002+ free_op2_string = true ;
19882003 }
19892004 } while (0 );
19902005
1991- if (UNEXPECTED (Z_STRLEN_P (op1 ) == 0 )) {
1992- if (EXPECTED (result != op2 )) {
2006+ has_op2_string :;
2007+ if (UNEXPECTED (ZSTR_LEN (op1_string ) == 0 )) {
2008+ if (EXPECTED (free_op2_string || result != op2 )) {
19932009 if (result == orig_op1 ) {
19942010 i_zval_ptr_dtor (result );
19952011 }
1996- ZVAL_COPY (result , op2 );
2012+ if (free_op2_string ) {
2013+ /* transfer ownership of op2_string */
2014+ ZVAL_STR (result , op2_string );
2015+ free_op2_string = false;
2016+ } else {
2017+ ZVAL_STR_COPY (result , op2_string );
2018+ }
19972019 }
1998- } else if (UNEXPECTED (Z_STRLEN_P ( op2 ) == 0 )) {
1999- if (EXPECTED (result != op1 )) {
2020+ } else if (UNEXPECTED (ZSTR_LEN ( op2_string ) == 0 )) {
2021+ if (EXPECTED (free_op1_string || result != op1 )) {
20002022 if (result == orig_op1 ) {
20012023 i_zval_ptr_dtor (result );
20022024 }
2003- ZVAL_COPY (result , op1 );
2025+ if (free_op1_string ) {
2026+ /* transfer ownership of op1_string */
2027+ ZVAL_STR (result , op1_string );
2028+ free_op1_string = false;
2029+ } else {
2030+ ZVAL_STR_COPY (result , op1_string );
2031+ }
20042032 }
20052033 } else {
2006- size_t op1_len = Z_STRLEN_P ( op1 );
2007- size_t op2_len = Z_STRLEN_P ( op2 );
2034+ size_t op1_len = ZSTR_LEN ( op1_string );
2035+ size_t op2_len = ZSTR_LEN ( op2_string );
20082036 size_t result_len = op1_len + op2_len ;
20092037 zend_string * result_str ;
2010- uint32_t flags = ZSTR_GET_COPYABLE_CONCAT_PROPERTIES_BOTH (Z_STR_P ( op1 ), Z_STR_P ( op2 ) );
2038+ uint32_t flags = ZSTR_GET_COPYABLE_CONCAT_PROPERTIES_BOTH (op1_string , op2_string );
20112039
20122040 if (UNEXPECTED (op1_len > ZSTR_MAX_LEN - op2_len )) {
2041+ if (free_op1_string ) zend_string_release (op1_string );
2042+ if (free_op2_string ) zend_string_release (op2_string );
20132043 zend_throw_error (NULL , "String size overflow" );
2014- zval_ptr_dtor_str (& op1_copy );
2015- zval_ptr_dtor_str (& op2_copy );
20162044 if (orig_op1 != result ) {
20172045 ZVAL_UNDEF (result );
20182046 }
20192047 return FAILURE ;
20202048 }
20212049
2022- if (result == op1 && Z_REFCOUNTED_P (result )) {
2050+ if (result == op1 ) {
2051+ if (free_op1_string ) {
2052+ /* op1_string will be used as the result, so we should not free it */
2053+ i_zval_ptr_dtor (result );
2054+ free_op1_string = false;
2055+ }
20232056 /* special case, perform operations on result */
2024- result_str = zend_string_extend (Z_STR_P (result ), result_len , 0 );
2057+ result_str = zend_string_extend (op1_string , result_len , 0 );
2058+ /* account for the case where result_str == op1_string == op2_string and the realloc is done */
2059+ if (op1_string == op2_string ) {
2060+ if (free_op2_string ) {
2061+ zend_string_release (op2_string );
2062+ free_op2_string = false;
2063+ }
2064+ op2_string = result_str ;
2065+ }
20252066 } else {
20262067 result_str = zend_string_alloc (result_len , 0 );
2027- memcpy (ZSTR_VAL (result_str ), Z_STRVAL_P ( op1 ), op1_len );
2068+ memcpy (ZSTR_VAL (result_str ), ZSTR_VAL ( op1_string ), op1_len );
20282069 if (result == orig_op1 ) {
20292070 i_zval_ptr_dtor (result );
20302071 }
20312072 }
20322073 GC_ADD_FLAGS (result_str , flags );
20332074
2034- /* This has to happen first to account for the cases where result == op1 == op2 and
2035- * the realloc is done. In this case this line will also update Z_STRVAL_P(op2) to
2036- * point to the new string. The first op2_len bytes of result will still be the same. */
20372075 ZVAL_NEW_STR (result , result_str );
2038-
2039- memcpy (ZSTR_VAL (result_str ) + op1_len , Z_STRVAL_P (op2 ), op2_len );
2076+ memcpy (ZSTR_VAL (result_str ) + op1_len , ZSTR_VAL (op2_string ), op2_len );
20402077 ZSTR_VAL (result_str )[result_len ] = '\0' ;
20412078 }
20422079
2043- zval_ptr_dtor_str (& op1_copy );
2044- zval_ptr_dtor_str (& op2_copy );
2080+ if (free_op1_string ) zend_string_release (op1_string );
2081+ if (free_op2_string ) zend_string_release (op2_string );
2082+
20452083 return SUCCESS ;
20462084}
20472085/* }}} */
0 commit comments