From d85ac7fb3f498455cfc1b051f8d48c9ba8ed8fdd Mon Sep 17 00:00:00 2001 From: Dmitry Stogov Date: Tue, 26 Nov 2013 17:47:02 +0400 Subject: [PATCH] Fixed bug #66176 (Invalid constant substitution) --- NEWS | 1 + ext/opcache/Optimizer/block_pass.c | 7 +- ext/opcache/Optimizer/pass1_5.c | 23 +-- ext/opcache/Optimizer/zend_optimizer.c | 228 +++++++++++++------------ ext/opcache/tests/bug66176.phpt | 19 +++ 5 files changed, 152 insertions(+), 126 deletions(-) create mode 100644 ext/opcache/tests/bug66176.phpt diff --git a/NEWS b/NEWS index 571de403dd..c835df3595 100644 --- a/NEWS +++ b/NEWS @@ -12,6 +12,7 @@ PHP NEWS string). (Laruence) - OPCache + . Fixed bug #66176 (Invalid constant substitution). (Dmitry) . Fixed bug #65915 (Inconsistent results with require return value). (Dmitry) . Fixed bug #65559 (Opcache: cache not cleared if changes occur while running). (Dmitry) diff --git a/ext/opcache/Optimizer/block_pass.c b/ext/opcache/Optimizer/block_pass.c index 1c34cffbf7..9f160539e9 100644 --- a/ext/opcache/Optimizer/block_pass.c +++ b/ext/opcache/Optimizer/block_pass.c @@ -1070,10 +1070,9 @@ static void zend_optimize_block(zend_code_block *block, zend_op_array *op_array, literal_dtor(&ZEND_OP1_LITERAL(opline)); literal_dtor(&ZEND_OP2_LITERAL(opline)); - ZEND_OP1_LITERAL(opline) = result; - SET_UNUSED(opline->op2); - opline->opcode = ZEND_QM_ASSIGN; + SET_UNUSED(opline->op2); + update_op1_const(op_array, opline, &result TSRMLS_CC); } EG(error_reporting) = er; } else if ((opline->opcode == ZEND_BOOL || @@ -1097,8 +1096,8 @@ static void zend_optimize_block(zend_code_block *block, zend_op_array *op_array, } PZ_SET_REFCOUNT_P(&result, 1); PZ_UNSET_ISREF_P(&result); - ZEND_OP1_LITERAL(opline) = result; opline->opcode = ZEND_QM_ASSIGN; + update_op1_const(op_array, opline, &result TSRMLS_CC); } else if ((opline->opcode == ZEND_RETURN || opline->opcode == ZEND_EXIT) && ZEND_OP1_TYPE(opline) == IS_TMP_VAR && VAR_SOURCE(opline->op1) && diff --git a/ext/opcache/Optimizer/pass1_5.c b/ext/opcache/Optimizer/pass1_5.c index 70ec6d5e2e..ca5b882901 100644 --- a/ext/opcache/Optimizer/pass1_5.c +++ b/ext/opcache/Optimizer/pass1_5.c @@ -219,15 +219,11 @@ if (ZEND_OPTIMIZER_PASS_1 & OPTIMIZATION_LEVEL) { EG(in_execution) = 1; EG(active_op_array) = op_array; if (zend_get_constant("__COMPILER_HALT_OFFSET__", sizeof("__COMPILER_HALT_OFFSET__") - 1, &offset TSRMLS_CC)) { + zend_uint tv = ZEND_RESULT(opline).var; + literal_dtor(&ZEND_OP2_LITERAL(opline)); - ZEND_OP1_TYPE(opline) = IS_CONST; -#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO - opline->op1.constant = zend_optimizer_add_literal(op_array, &offset TSRMLS_CC); -#else - ZEND_OP1_LITERAL(opline) = offset; -#endif - SET_UNUSED(opline->op2); - opline->opcode = ZEND_QM_ASSIGN; + MAKE_NOP(opline); + replace_tmp_by_const(op_array, opline, tv, &offset TSRMLS_CC); } EG(active_op_array) = orig_op_array; EG(in_execution) = orig_in_execution; @@ -238,20 +234,15 @@ if (ZEND_OPTIMIZER_PASS_1 & OPTIMIZATION_LEVEL) { ZEND_OP2_TYPE(opline) == IS_CONST && ZEND_OP2_LITERAL(opline).type == IS_STRING) { /* substitute persistent constants */ + zend_uint tv = ZEND_RESULT(opline).var; zval c; if (!zend_get_persistent_constant(Z_STRVAL(ZEND_OP2_LITERAL(opline)), Z_STRLEN(ZEND_OP2_LITERAL(opline)), &c, 1 TSRMLS_CC)) { break; } literal_dtor(&ZEND_OP2_LITERAL(opline)); - ZEND_OP1_TYPE(opline) = IS_CONST; -#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO - opline->op1.constant = zend_optimizer_add_literal(op_array, &c TSRMLS_CC); -#else - ZEND_OP1_LITERAL(opline) = c; -#endif - SET_UNUSED(opline->op2); - opline->opcode = ZEND_QM_ASSIGN; + MAKE_NOP(opline); + replace_tmp_by_const(op_array, opline, tv, &c TSRMLS_CC); } break; diff --git a/ext/opcache/Optimizer/zend_optimizer.c b/ext/opcache/Optimizer/zend_optimizer.c index 28085cb441..c7fbad1189 100644 --- a/ext/opcache/Optimizer/zend_optimizer.c +++ b/ext/opcache/Optimizer/zend_optimizer.c @@ -19,6 +19,7 @@ +----------------------------------------------------------------------+ */ +#include "php.h" #include "Optimizer/zend_optimizer.h" #include "Optimizer/zend_optimizer_internal.h" #include "zend_API.h" @@ -110,6 +111,124 @@ int zend_optimizer_add_literal(zend_op_array *op_array, const zval *zv TSRMLS_DC #endif +static void update_op1_const(zend_op_array *op_array, + zend_op *opline, + zval *val TSRMLS_DC) +{ + if (opline->opcode == ZEND_FREE) { + MAKE_NOP(opline); + zval_dtor(val); + } else { + ZEND_OP1_TYPE(opline) = IS_CONST; +#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO + if (Z_TYPE_P(val) == IS_STRING) { + switch (opline->opcode) { + case ZEND_INIT_STATIC_METHOD_CALL: + case ZEND_CATCH: + case ZEND_FETCH_CONSTANT: + opline->op1.constant = zend_optimizer_add_literal(op_array, val TSRMLS_CC); + Z_HASH_P(&ZEND_OP1_LITERAL(opline)) = zend_hash_func(Z_STRVAL(ZEND_OP1_LITERAL(opline)), Z_STRLEN(ZEND_OP1_LITERAL(opline)) + 1); + op_array->literals[opline->op1.constant].cache_slot = op_array->last_cache_slot++; + zend_str_tolower(Z_STRVAL_P(val), Z_STRLEN_P(val)); + zend_optimizer_add_literal(op_array, val TSRMLS_CC); + op_array->literals[opline->op1.constant+1].hash_value = zend_hash_func(Z_STRVAL(op_array->literals[opline->op1.constant+1].constant), Z_STRLEN(op_array->literals[opline->op1.constant+1].constant) + 1); + break; + case ZEND_DO_FCALL: + zend_str_tolower(Z_STRVAL_P(val), Z_STRLEN_P(val)); + opline->op1.constant = zend_optimizer_add_literal(op_array, val TSRMLS_CC); + Z_HASH_P(&ZEND_OP1_LITERAL(opline)) = zend_hash_func(Z_STRVAL(ZEND_OP1_LITERAL(opline)), Z_STRLEN(ZEND_OP1_LITERAL(opline)) + 1); + op_array->literals[opline->op1.constant].cache_slot = op_array->last_cache_slot++; + break; + default: + opline->op1.constant = zend_optimizer_add_literal(op_array, val TSRMLS_CC); + Z_HASH_P(&ZEND_OP1_LITERAL(opline)) = zend_hash_func(Z_STRVAL(ZEND_OP1_LITERAL(opline)), Z_STRLEN(ZEND_OP1_LITERAL(opline)) + 1); + break; + } + } else { + opline->op1.constant = zend_optimizer_add_literal(op_array, val TSRMLS_CC); + } +#else + ZEND_OP1_LITERAL(opline) = *val; +#endif + } +} + +static void update_op2_const(zend_op_array *op_array, + zend_op *opline, + zval *val TSRMLS_DC) +{ + ZEND_OP2_TYPE(opline) = IS_CONST; +#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO + opline->op2.constant = zend_optimizer_add_literal(op_array, val TSRMLS_CC); + if (Z_TYPE_P(val) == IS_STRING) { + Z_HASH_P(&ZEND_OP2_LITERAL(opline)) = zend_hash_func(Z_STRVAL(ZEND_OP2_LITERAL(opline)), Z_STRLEN(ZEND_OP2_LITERAL(opline)) + 1); + switch (opline->opcode) { + case ZEND_FETCH_R: + case ZEND_FETCH_W: + case ZEND_FETCH_RW: + case ZEND_FETCH_IS: + case ZEND_FETCH_UNSET: + case ZEND_FETCH_FUNC_ARG: + case ZEND_FETCH_CLASS: + case ZEND_INIT_FCALL_BY_NAME: + /*case ZEND_INIT_NS_FCALL_BY_NAME:*/ + case ZEND_UNSET_VAR: + case ZEND_ISSET_ISEMPTY_VAR: + case ZEND_ADD_INTERFACE: + case ZEND_ADD_TRAIT: + op_array->literals[opline->op2.constant].cache_slot = op_array->last_cache_slot++; + zend_str_tolower(Z_STRVAL_P(val), Z_STRLEN_P(val)); + zend_optimizer_add_literal(op_array, val TSRMLS_CC); + op_array->literals[opline->op2.constant+1].hash_value = zend_hash_func(Z_STRVAL(op_array->literals[opline->op2.constant+1].constant), Z_STRLEN(op_array->literals[opline->op2.constant+1].constant) + 1); + break; + case ZEND_INIT_METHOD_CALL: + case ZEND_INIT_STATIC_METHOD_CALL: + zend_str_tolower(Z_STRVAL_P(val), Z_STRLEN_P(val)); + zend_optimizer_add_literal(op_array, val TSRMLS_CC); + op_array->literals[opline->op2.constant+1].hash_value = zend_hash_func(Z_STRVAL(op_array->literals[opline->op2.constant+1].constant), Z_STRLEN(op_array->literals[opline->op2.constant+1].constant) + 1); + /* break missing intentionally */ + /*case ZEND_FETCH_CONSTANT:*/ + case ZEND_ASSIGN_OBJ: + case ZEND_FETCH_OBJ_R: + case ZEND_FETCH_OBJ_W: + case ZEND_FETCH_OBJ_RW: + case ZEND_FETCH_OBJ_IS: + case ZEND_FETCH_OBJ_UNSET: + case ZEND_FETCH_OBJ_FUNC_ARG: + case ZEND_UNSET_OBJ: + case ZEND_PRE_INC_OBJ: + case ZEND_PRE_DEC_OBJ: + case ZEND_POST_INC_OBJ: + case ZEND_POST_DEC_OBJ: + case ZEND_ISSET_ISEMPTY_PROP_OBJ: + op_array->literals[opline->op2.constant].cache_slot = op_array->last_cache_slot; + op_array->last_cache_slot += 2; + break; + case ZEND_ASSIGN_ADD: + case ZEND_ASSIGN_SUB: + case ZEND_ASSIGN_MUL: + case ZEND_ASSIGN_DIV: + case ZEND_ASSIGN_MOD: + case ZEND_ASSIGN_SL: + case ZEND_ASSIGN_SR: + case ZEND_ASSIGN_CONCAT: + case ZEND_ASSIGN_BW_OR: + case ZEND_ASSIGN_BW_AND: + case ZEND_ASSIGN_BW_XOR: + if (opline->extended_value == ZEND_ASSIGN_OBJ) { + op_array->literals[opline->op2.constant].cache_slot = op_array->last_cache_slot; + op_array->last_cache_slot += 2; + } + break; + default: + break; + } + } +#else + ZEND_OP2_LITERAL(opline) = *val; +#endif +} + static void replace_tmp_by_const(zend_op_array *op_array, zend_op *opline, zend_uint var, @@ -122,42 +241,7 @@ static void replace_tmp_by_const(zend_op_array *op_array, if (ZEND_OP1_TYPE(opline) == IS_TMP_VAR && ZEND_OP1(opline).var == var) { - if (opline->opcode == ZEND_FREE) { - MAKE_NOP(opline); - zval_dtor(val); - } else { - ZEND_OP1_TYPE(opline) = IS_CONST; -#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO - if (Z_TYPE_P(val) == IS_STRING) { - switch (opline->opcode) { - case ZEND_INIT_STATIC_METHOD_CALL: - case ZEND_CATCH: - case ZEND_FETCH_CONSTANT: - opline->op1.constant = zend_optimizer_add_literal(op_array, val TSRMLS_CC); - Z_HASH_P(&ZEND_OP1_LITERAL(opline)) = zend_hash_func(Z_STRVAL(ZEND_OP1_LITERAL(opline)), Z_STRLEN(ZEND_OP1_LITERAL(opline)) + 1); - op_array->literals[opline->op1.constant].cache_slot = op_array->last_cache_slot++; - zend_str_tolower(Z_STRVAL_P(val), Z_STRLEN_P(val)); - zend_optimizer_add_literal(op_array, val TSRMLS_CC); - op_array->literals[opline->op1.constant+1].hash_value = zend_hash_func(Z_STRVAL(op_array->literals[opline->op1.constant+1].constant), Z_STRLEN(op_array->literals[opline->op1.constant+1].constant) + 1); - break; - case ZEND_DO_FCALL: - zend_str_tolower(Z_STRVAL_P(val), Z_STRLEN_P(val)); - opline->op1.constant = zend_optimizer_add_literal(op_array, val TSRMLS_CC); - Z_HASH_P(&ZEND_OP1_LITERAL(opline)) = zend_hash_func(Z_STRVAL(ZEND_OP1_LITERAL(opline)), Z_STRLEN(ZEND_OP1_LITERAL(opline)) + 1); - op_array->literals[opline->op1.constant].cache_slot = op_array->last_cache_slot++; - break; - default: - opline->op1.constant = zend_optimizer_add_literal(op_array, val TSRMLS_CC); - Z_HASH_P(&ZEND_OP1_LITERAL(opline)) = zend_hash_func(Z_STRVAL(ZEND_OP1_LITERAL(opline)), Z_STRLEN(ZEND_OP1_LITERAL(opline)) + 1); - break; - } - } else { - opline->op1.constant = zend_optimizer_add_literal(op_array, val TSRMLS_CC); - } -#else - ZEND_OP1_LITERAL(opline) = *val; -#endif - } + update_op1_const(op_array, opline, val TSRMLS_CC); /* TMP_VAR my be used only once */ break; } @@ -165,76 +249,8 @@ static void replace_tmp_by_const(zend_op_array *op_array, if (ZEND_OP2_TYPE(opline) == IS_TMP_VAR && ZEND_OP2(opline).var == var) { - ZEND_OP2_TYPE(opline) = IS_CONST; -#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO - opline->op2.constant = zend_optimizer_add_literal(op_array, val TSRMLS_CC); - if (Z_TYPE_P(val) == IS_STRING) { - Z_HASH_P(&ZEND_OP2_LITERAL(opline)) = zend_hash_func(Z_STRVAL(ZEND_OP2_LITERAL(opline)), Z_STRLEN(ZEND_OP2_LITERAL(opline)) + 1); - switch (opline->opcode) { - case ZEND_FETCH_R: - case ZEND_FETCH_W: - case ZEND_FETCH_RW: - case ZEND_FETCH_IS: - case ZEND_FETCH_UNSET: - case ZEND_FETCH_FUNC_ARG: - case ZEND_FETCH_CLASS: - case ZEND_INIT_FCALL_BY_NAME: - /*case ZEND_INIT_NS_FCALL_BY_NAME:*/ - case ZEND_UNSET_VAR: - case ZEND_ISSET_ISEMPTY_VAR: - case ZEND_ADD_INTERFACE: - case ZEND_ADD_TRAIT: - op_array->literals[opline->op2.constant].cache_slot = op_array->last_cache_slot++; - zend_str_tolower(Z_STRVAL_P(val), Z_STRLEN_P(val)); - zend_optimizer_add_literal(op_array, val TSRMLS_CC); - op_array->literals[opline->op2.constant+1].hash_value = zend_hash_func(Z_STRVAL(op_array->literals[opline->op2.constant+1].constant), Z_STRLEN(op_array->literals[opline->op2.constant+1].constant) + 1); - break; - case ZEND_INIT_METHOD_CALL: - case ZEND_INIT_STATIC_METHOD_CALL: - zend_str_tolower(Z_STRVAL_P(val), Z_STRLEN_P(val)); - zend_optimizer_add_literal(op_array, val TSRMLS_CC); - op_array->literals[opline->op2.constant+1].hash_value = zend_hash_func(Z_STRVAL(op_array->literals[opline->op2.constant+1].constant), Z_STRLEN(op_array->literals[opline->op2.constant+1].constant) + 1); - /* break missing intentionally */ - /*case ZEND_FETCH_CONSTANT:*/ - case ZEND_ASSIGN_OBJ: - case ZEND_FETCH_OBJ_R: - case ZEND_FETCH_OBJ_W: - case ZEND_FETCH_OBJ_RW: - case ZEND_FETCH_OBJ_IS: - case ZEND_FETCH_OBJ_UNSET: - case ZEND_FETCH_OBJ_FUNC_ARG: - case ZEND_UNSET_OBJ: - case ZEND_PRE_INC_OBJ: - case ZEND_PRE_DEC_OBJ: - case ZEND_POST_INC_OBJ: - case ZEND_POST_DEC_OBJ: - case ZEND_ISSET_ISEMPTY_PROP_OBJ: - op_array->literals[opline->op2.constant].cache_slot = op_array->last_cache_slot; - op_array->last_cache_slot += 2; - break; - case ZEND_ASSIGN_ADD: - case ZEND_ASSIGN_SUB: - case ZEND_ASSIGN_MUL: - case ZEND_ASSIGN_DIV: - case ZEND_ASSIGN_MOD: - case ZEND_ASSIGN_SL: - case ZEND_ASSIGN_SR: - case ZEND_ASSIGN_CONCAT: - case ZEND_ASSIGN_BW_OR: - case ZEND_ASSIGN_BW_AND: - case ZEND_ASSIGN_BW_XOR: - if (opline->extended_value == ZEND_ASSIGN_OBJ) { - op_array->literals[opline->op2.constant].cache_slot = op_array->last_cache_slot; - op_array->last_cache_slot += 2; - } - break; - default: - break; - } - } -#else - ZEND_OP2_LITERAL(opline) = *val; -#endif + update_op2_const(op_array, opline, val TSRMLS_CC); + /* TMP_VAR my be used only once */ break; } opline++; diff --git a/ext/opcache/tests/bug66176.phpt b/ext/opcache/tests/bug66176.phpt new file mode 100644 index 0000000000..a91024a616 --- /dev/null +++ b/ext/opcache/tests/bug66176.phpt @@ -0,0 +1,19 @@ +--TEST-- +Bug #66176 (Invalid constant substitution) +--INI-- +opcache.enable=1 +opcache.enable_cli=1 +opcache.optimization_level=-1 +opcache.file_update_protection=0 +--SKIPIF-- + +--FILE-- + 1); +var_dump(foo(PHP_VERSION)); +--EXPECT-- +int(1) -- 2.40.0