@@ -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