diff --git a/Zend/zend.c b/Zend/zend.c index d85135d2871..3561b36af12 100644 --- a/Zend/zend.c +++ b/Zend/zend.c @@ -524,6 +524,8 @@ static void zend_set_default_compile_time_values(void) /* {{{ */ /* default compile-time values */ CG(short_tags) = short_tags_default; CG(compiler_options) = compiler_options_default; + + CG(rtd_key_counter) = 0; } /* }}} */ diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index 311d3d27279..43edd438bbb 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -132,16 +132,11 @@ static void zend_destroy_property_info_internal(zval *zv) /* {{{ */ } /* }}} */ -static zend_string *zend_build_runtime_definition_key(zend_string *name, unsigned char *lex_pos) /* {{{ */ +static zend_string *zend_build_runtime_definition_key(zend_string *name, uint32_t start_lineno) /* {{{ */ { - zend_string *result; - char char_pos_buf[32]; - size_t char_pos_len = sprintf(char_pos_buf, "%p", lex_pos); zend_string *filename = CG(active_op_array)->filename; - - /* NULL, name length, filename length, last accepting char position length */ - result = zend_string_alloc(1 + ZSTR_LEN(name) + ZSTR_LEN(filename) + char_pos_len, 0); - sprintf(ZSTR_VAL(result), "%c%s%s%s", '\0', ZSTR_VAL(name), ZSTR_VAL(filename), char_pos_buf); + zend_string *result = zend_strpprintf(0, "%c%s%s:%" PRIu32 "$%" PRIx32, + '\0', ZSTR_VAL(name), ZSTR_VAL(filename), start_lineno, CG(rtd_key_counter)++); return zend_new_interned_string(result); } /* }}} */ @@ -6124,8 +6119,11 @@ static void zend_begin_func_decl(znode *result, zend_op_array *op_array, zend_as return; } - key = zend_build_runtime_definition_key(lcname, decl->lex_pos); - zend_hash_update_ptr(CG(function_table), key, op_array); + key = zend_build_runtime_definition_key(lcname, decl->start_lineno); + if (!zend_hash_add_ptr(CG(function_table), key, op_array)) { + zend_error_noreturn(E_ERROR, + "Runtime definition key collision for function %s. This is a bug", ZSTR_VAL(name)); + } if (op_array->fn_flags & ZEND_ACC_CLOSURE) { opline = zend_emit_op_tmp(result, ZEND_DECLARE_LAMBDA_FUNCTION, NULL, NULL); @@ -6529,16 +6527,11 @@ void zend_compile_implements(zend_ast *ast) /* {{{ */ } /* }}} */ -static zend_string *zend_generate_anon_class_name(unsigned char *lex_pos) /* {{{ */ +static zend_string *zend_generate_anon_class_name(uint32_t start_lineno) /* {{{ */ { - zend_string *result; - char char_pos_buf[32]; - size_t char_pos_len = sprintf(char_pos_buf, "%p", lex_pos); zend_string *filename = CG(active_op_array)->filename; - - /* NULL, name length, filename length, last accepting char position length */ - result = zend_string_alloc(sizeof("class@anonymous") + ZSTR_LEN(filename) + char_pos_len, 0); - sprintf(ZSTR_VAL(result), "class@anonymous%c%s%s", '\0', ZSTR_VAL(filename), char_pos_buf); + zend_string *result = zend_strpprintf(0, "class@anonymous%c%s:%" PRIu32 "$%" PRIx32, + '\0', ZSTR_VAL(filename), start_lineno, CG(rtd_key_counter)++); return zend_new_interned_string(result); } /* }}} */ @@ -6578,7 +6571,7 @@ zend_op *zend_compile_class_decl(zend_ast *ast, zend_bool toplevel) /* {{{ */ zend_register_seen_symbol(lcname, ZEND_SYMBOL_CLASS); } else { - name = zend_generate_anon_class_name(decl->lex_pos); + name = zend_generate_anon_class_name(decl->start_lineno); lcname = zend_string_tolower(name); } lcname = zend_new_interned_string(lcname); @@ -6728,20 +6721,19 @@ zend_op *zend_compile_class_decl(zend_ast *ast, zend_bool toplevel) /* {{{ */ opline->extended_value = zend_alloc_cache_slot(); opline->result_type = IS_VAR; opline->result.var = get_temporary_variable(); - if (!zend_hash_add_ptr(CG(class_table), lcname, ce)) { - /* this anonymous class has been included */ - zval zv; - ZVAL_PTR(&zv, ce); - destroy_zend_class(&zv); - return opline; + zend_error_noreturn(E_ERROR, + "Runtime definition key collision for %s. This is a bug", ZSTR_VAL(name)); } } else { - zend_string *key = zend_build_runtime_definition_key(lcname, decl->lex_pos); + zend_string *key = zend_build_runtime_definition_key(lcname, decl->start_lineno); /* RTD key is placed after lcname literal in op1 */ zend_add_literal_string(&key); - zend_hash_update_ptr(CG(class_table), key, ce); + if (!zend_hash_add_ptr(CG(class_table), key, ce)) { + zend_error_noreturn(E_ERROR, + "Runtime definition key collision for class %s. This is a bug", ZSTR_VAL(name)); + } opline->opcode = ZEND_DECLARE_CLASS; if (extends_ast && toplevel diff --git a/Zend/zend_globals.h b/Zend/zend_globals.h index 9a55924f428..2dc769a5efe 100644 --- a/Zend/zend_globals.h +++ b/Zend/zend_globals.h @@ -127,6 +127,8 @@ struct _zend_compiler_globals { HashTable *delayed_variance_obligations; HashTable *delayed_autoloads; + + uint32_t rtd_key_counter; }; diff --git a/ext/opcache/zend_accelerator_util_funcs.c b/ext/opcache/zend_accelerator_util_funcs.c index 9eb6745478f..72caad64217 100644 --- a/ext/opcache/zend_accelerator_util_funcs.c +++ b/ext/opcache/zend_accelerator_util_funcs.c @@ -439,8 +439,16 @@ static void zend_accel_function_hash_copy(HashTable *target, HashTable *source) t = zend_hash_find_ex(target, p->key, 1); if (UNEXPECTED(t != NULL)) { if (EXPECTED(ZSTR_LEN(p->key) > 0) && EXPECTED(ZSTR_VAL(p->key)[0] == 0)) { - /* Mangled key */ - t = zend_hash_update(target, p->key, &p->val); + /* Runtime definition key. There are two circumstances under which the key can + * already be defined: + * 1. The file has been re-included without being changed in the meantime. In + * this case we can keep the old value, because we know that the definition + * hasn't changed. + * 2. The file has been changed in the meantime, but the RTD key ends up colliding. + * This would be a bug. + * As we can't distinguish these cases, we assume that it is 1. and keep the old + * value. */ + continue; } else { goto failure; } @@ -483,8 +491,8 @@ static void zend_accel_function_hash_copy_from_shm(HashTable *target, HashTable t = zend_hash_find_ex(target, p->key, 1); if (UNEXPECTED(t != NULL)) { if (EXPECTED(ZSTR_LEN(p->key) > 0) && EXPECTED(ZSTR_VAL(p->key)[0] == 0)) { - /* Mangled key */ - zend_hash_update_ptr(target, p->key, Z_PTR(p->val)); + /* See comment in zend_accel_function_hash_copy(). */ + continue; } else { goto failure; } @@ -526,7 +534,7 @@ static void zend_accel_class_hash_copy(HashTable *target, HashTable *source) t = zend_hash_find_ex(target, p->key, 1); if (UNEXPECTED(t != NULL)) { if (EXPECTED(ZSTR_LEN(p->key) > 0) && EXPECTED(ZSTR_VAL(p->key)[0] == 0)) { - /* Mangled key - ignore and wait for runtime */ + /* See comment in zend_accel_function_hash_copy(). */ continue; } else if (UNEXPECTED(!ZCG(accel_directives).ignore_dups)) { zend_class_entry *ce1 = Z_PTR(p->val); @@ -563,7 +571,7 @@ static void zend_accel_class_hash_copy_from_shm(HashTable *target, HashTable *so t = zend_hash_find_ex(target, p->key, 1); if (UNEXPECTED(t != NULL)) { if (EXPECTED(ZSTR_LEN(p->key) > 0) && EXPECTED(ZSTR_VAL(p->key)[0] == 0)) { - /* Mangled key - ignore and wait for runtime */ + /* See comment in zend_accel_function_hash_copy(). */ continue; } else if (UNEXPECTED(!ZCG(accel_directives).ignore_dups)) { zend_class_entry *ce1 = Z_PTR(p->val);