Skip to content

Commit

Permalink
Fixed bug 78175 (Preloading must store default values of static varia…
Browse files Browse the repository at this point in the history
…bles and properties)
  • Loading branch information
dstogov committed Jun 24, 2019
1 parent 94df6dc commit 0f29fb5
Show file tree
Hide file tree
Showing 11 changed files with 127 additions and 16 deletions.
15 changes: 13 additions & 2 deletions Zend/zend_compile.c
Expand Up @@ -5877,8 +5877,14 @@ void zend_compile_func_decl(znode *result, zend_ast *ast, zend_bool toplevel) /*

init_op_array(op_array, ZEND_USER_FUNCTION, INITIAL_OP_ARRAY_SIZE);

ZEND_MAP_PTR_INIT(op_array->run_time_cache, zend_arena_alloc(&CG(arena), sizeof(void*)));
ZEND_MAP_PTR_SET(op_array->run_time_cache, NULL);
if (CG(compiler_options) & ZEND_COMPILE_PRELOAD) {
op_array->fn_flags |= ZEND_ACC_PRELOADED;
ZEND_MAP_PTR_NEW(op_array->run_time_cache);
ZEND_MAP_PTR_NEW(op_array->static_variables_ptr);
} else {
ZEND_MAP_PTR_INIT(op_array->run_time_cache, zend_arena_alloc(&CG(arena), sizeof(void*)));
ZEND_MAP_PTR_SET(op_array->run_time_cache, NULL);
}

op_array->fn_flags |= (orig_op_array->fn_flags & ZEND_ACC_STRICT_TYPES);
op_array->fn_flags |= decl->flags;
Expand Down Expand Up @@ -6313,6 +6319,11 @@ zend_op *zend_compile_class_decl(zend_ast *ast, zend_bool toplevel) /* {{{ */
ce->name = name;
zend_initialize_class_data(ce, 1);

if (CG(compiler_options) & ZEND_COMPILE_PRELOAD) {
ce->ce_flags |= ZEND_ACC_PRELOADED;
ZEND_MAP_PTR_NEW(ce->static_members_table);
}

ce->ce_flags |= decl->flags;
ce->info.user.filename = zend_get_compiled_filename();
ce->info.user.line_start = decl->start_lineno;
Expand Down
2 changes: 1 addition & 1 deletion Zend/zend_inheritance.c
Expand Up @@ -1178,7 +1178,7 @@ ZEND_API void zend_do_inheritance(zend_class_entry *ce, zend_class_entry *parent
} while (dst != end);
} else if (ce->type == ZEND_USER_CLASS) {
if (CE_STATIC_MEMBERS(parent_ce) == NULL) {
ZEND_ASSERT(parent_ce->ce_flags & ZEND_ACC_IMMUTABLE);
ZEND_ASSERT(parent_ce->ce_flags & (ZEND_ACC_IMMUTABLE|ZEND_ACC_PRELOADED));
zend_class_init_statics(parent_ce);
}
src = CE_STATIC_MEMBERS(parent_ce) + parent_ce->default_static_members_count;
Expand Down
2 changes: 1 addition & 1 deletion Zend/zend_object_handlers.c
Expand Up @@ -1491,7 +1491,7 @@ ZEND_API zval *zend_std_get_static_property_with_info(zend_class_entry *ce, zend

/* check if static properties were destroyed */
if (UNEXPECTED(CE_STATIC_MEMBERS(ce) == NULL)) {
if (ce->type == ZEND_INTERNAL_CLASS || (ce->ce_flags & ZEND_ACC_IMMUTABLE)) {
if (ce->type == ZEND_INTERNAL_CLASS || (ce->ce_flags & (ZEND_ACC_IMMUTABLE|ZEND_ACC_PRELOADED))) {
zend_class_init_statics(ce);
} else {
undeclared_property:
Expand Down
2 changes: 1 addition & 1 deletion Zend/zend_opcode.c
Expand Up @@ -219,7 +219,7 @@ ZEND_API void destroy_zend_class(zval *zv)
zend_class_entry *ce = Z_PTR_P(zv);
zend_function *fn;

if (ce->ce_flags & ZEND_ACC_IMMUTABLE) {
if (ce->ce_flags & (ZEND_ACC_IMMUTABLE|ZEND_ACC_PRELOADED)) {
zend_op_array *op_array;

if (ce->default_static_members_count) {
Expand Down
2 changes: 1 addition & 1 deletion Zend/zend_vm_def.h
Expand Up @@ -8122,7 +8122,7 @@ ZEND_VM_HANDLER(183, ZEND_BIND_STATIC, CV, UNUSED, REF)

ht = ZEND_MAP_PTR_GET(EX(func)->op_array.static_variables_ptr);
if (!ht) {
ZEND_ASSERT(EX(func)->op_array.fn_flags & ZEND_ACC_IMMUTABLE);
ZEND_ASSERT(EX(func)->op_array.fn_flags & (ZEND_ACC_IMMUTABLE|ZEND_ACC_PRELOADED));
ht = zend_array_dup(EX(func)->op_array.static_variables);
ZEND_MAP_PTR_SET(EX(func)->op_array.static_variables_ptr, ht);
} else if (GC_REFCOUNT(ht) > 1) {
Expand Down
2 changes: 1 addition & 1 deletion Zend/zend_vm_execute.h
Expand Up @@ -47533,7 +47533,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_BIND_STATIC_SPEC_CV_UNUSED_HAN

ht = ZEND_MAP_PTR_GET(EX(func)->op_array.static_variables_ptr);
if (!ht) {
ZEND_ASSERT(EX(func)->op_array.fn_flags & ZEND_ACC_IMMUTABLE);
ZEND_ASSERT(EX(func)->op_array.fn_flags & (ZEND_ACC_IMMUTABLE|ZEND_ACC_PRELOADED));
ht = zend_array_dup(EX(func)->op_array.static_variables);
ZEND_MAP_PTR_SET(EX(func)->op_array.static_variables_ptr, ht);
} else if (GC_REFCOUNT(ht) > 1) {
Expand Down
71 changes: 62 additions & 9 deletions ext/opcache/ZendAccelerator.c
Expand Up @@ -3176,7 +3176,6 @@ static void preload_move_user_functions(HashTable *src, HashTable *dst)
}
if (copy) {
_zend_hash_append_ptr(dst, p->key, function);
function->common.fn_flags |= ZEND_ACC_PRELOADED;
} else {
orig_dtor(&p->val);
}
Expand Down Expand Up @@ -3210,15 +3209,7 @@ static void preload_move_user_classes(HashTable *src, HashTable *dst)
}
}
if (copy) {
zend_function *function;

ce->ce_flags |= ZEND_ACC_PRELOADED;
_zend_hash_append_ptr(dst, p->key, ce);
ZEND_HASH_FOREACH_PTR(&ce->function_table, function) {
if (EXPECTED(function->type == ZEND_USER_FUNCTION)) {
function->common.fn_flags |= ZEND_ACC_PRELOADED;
}
} ZEND_HASH_FOREACH_END();
} else {
orig_dtor(&p->val);
}
Expand Down Expand Up @@ -3481,6 +3472,7 @@ static void preload_link(void)
uint32_t i;
zend_op_array *op_array;
dtor_func_t orig_dtor;
zend_function *function;

/* Resolve class dependencies */
do {
Expand Down Expand Up @@ -3652,6 +3644,13 @@ static void preload_link(void)
} else {
continue;
}
ce->ce_flags &= ~ZEND_ACC_PRELOADED;
ZEND_HASH_FOREACH_PTR(&ce->function_table, function) {
if (EXPECTED(function->type == ZEND_USER_FUNCTION)
&& function->common.scope == ce) {
function->common.fn_flags &= ~ZEND_ACC_PRELOADED;
}
} ZEND_HASH_FOREACH_END();
script = zend_hash_find_ptr(preload_scripts, ce->info.user.filename);
ZEND_ASSERT(script);
zend_hash_add(&script->script.class_table, key, zv);
Expand Down Expand Up @@ -4006,6 +4005,8 @@ static int accel_preload(const char *config)
int ret;
uint32_t orig_compiler_options;
char *orig_open_basedir;
size_t orig_map_ptr_last;
zval *zv;

ZCG(enabled) = 0;
ZCG(accelerator_enabled) = 0;
Expand All @@ -4022,6 +4023,8 @@ static int accel_preload(const char *config)
CG(compiler_options) |= ZEND_COMPILE_NO_CONSTANT_SUBSTITUTION;
// CG(compiler_options) |= ZEND_COMPILE_IGNORE_OTHER_FILES;

orig_map_ptr_last = CG(map_ptr_last);

/* Compile and execute proloading script */
memset(&file_handle, 0, sizeof(file_handle));
file_handle.filename = (char*)config;
Expand Down Expand Up @@ -4105,10 +4108,58 @@ static int accel_preload(const char *config)

zend_objects_store_free_object_storage(&EG(objects_store), 1);

/* Cleanup static variables of preloaded functions */
ZEND_HASH_REVERSE_FOREACH_VAL(EG(function_table), zv) {
zend_op_array *op_array = Z_PTR_P(zv);
if (op_array->type == ZEND_INTERNAL_FUNCTION) {
break;
}
ZEND_ASSERT(op_array->fn_flags & ZEND_ACC_PRELOADED);
if (op_array->static_variables) {
HashTable *ht = ZEND_MAP_PTR_GET(op_array->static_variables_ptr);
if (ht) {
ZEND_ASSERT(GC_REFCOUNT(ht) == 1);
zend_array_destroy(ht);
ZEND_MAP_PTR_SET(op_array->static_variables_ptr, NULL);
}
}
} ZEND_HASH_FOREACH_END();

/* Cleanup static properties and variables of preloaded classes */
ZEND_HASH_REVERSE_FOREACH_VAL(EG(class_table), zv) {
zend_class_entry *ce = Z_PTR_P(zv);
if (ce->type == ZEND_INTERNAL_CLASS) {
break;
}
ZEND_ASSERT(ce->ce_flags & ZEND_ACC_PRELOADED);
if (ce->default_static_members_count) {
zend_cleanup_internal_class_data(ce);
}
if (ce->ce_flags & ZEND_HAS_STATIC_IN_METHODS) {
zend_op_array *op_array;

ZEND_HASH_FOREACH_PTR(&ce->function_table, op_array) {
if (op_array->type == ZEND_USER_FUNCTION) {
if (op_array->static_variables) {
HashTable *ht = ZEND_MAP_PTR_GET(op_array->static_variables_ptr);
if (ht) {
ZEND_ASSERT(GC_REFCOUNT(ht) == 1);
zend_array_destroy(ht);
ZEND_MAP_PTR_SET(op_array->static_variables_ptr, NULL);
}
}
}
} ZEND_HASH_FOREACH_END();
}
} ZEND_HASH_FOREACH_END();

CG(map_ptr_last) = orig_map_ptr_last;

/* Inheritance errors may be thrown during linking */
zend_try {
preload_link();
} zend_catch {
CG(map_ptr_last) = orig_map_ptr_last;
ret = FAILURE;
goto finish;
} zend_end_try();
Expand Down Expand Up @@ -4234,6 +4285,8 @@ static int accel_preload(const char *config)
HANDLE_UNBLOCK_INTERRUPTIONS();

zend_shared_alloc_destroy_xlat_table();
} else {
CG(map_ptr_last) = orig_map_ptr_last;
}

finish:
Expand Down
19 changes: 19 additions & 0 deletions ext/opcache/tests/bug78175_2.phpt
@@ -0,0 +1,19 @@
--TEST--
Bug #78175.2 (Preloading segfaults at preload time and at runtime)
--INI--
opcache.enable=1
opcache.enable_cli=1
opcache.optimization_level=-1
opcache.preload={PWD}/preload_bug78175_2.inc
--SKIPIF--
<?php require_once('skipif.inc'); ?>
--FILE--
<?php
var_dump(get_class(Loader::getLoader()));
var_dump(Loader::getCounter());
?>
OK
--EXPECT--
string(6) "Loader"
int(0)
OK
20 changes: 20 additions & 0 deletions ext/opcache/tests/preload_bug78175_2.inc
@@ -0,0 +1,20 @@
<?php
class Loader {
static private $loader;

static function getLoader() {
if (null !== self::$loader) {
return self::$loader;
}
return self::$loader = new Loader();
}

static function getCounter() {
static $counter = 0;
return $counter++;
}
}

Loader::getLoader();
Loader::getCounter();
Loader::getCounter();
4 changes: 4 additions & 0 deletions ext/opcache/zend_persist.c
Expand Up @@ -247,6 +247,10 @@ static void zend_persist_zval(zval *z)
efree(old_ref);
}
break;
default:
ZEND_ASSERT(Z_TYPE_P(z) != IS_OBJECT);
ZEND_ASSERT(Z_TYPE_P(z) != IS_RESOURCE);
break;
}
}

Expand Down
4 changes: 4 additions & 0 deletions ext/opcache/zend_persist_calc.c
Expand Up @@ -141,6 +141,10 @@ static void zend_persist_zval_calc(zval *z)
zend_persist_ast_calc(Z_ASTVAL_P(z));
}
break;
default:
ZEND_ASSERT(Z_TYPE_P(z) != IS_OBJECT);
ZEND_ASSERT(Z_TYPE_P(z) != IS_RESOURCE);
break;
}
}

Expand Down

0 comments on commit 0f29fb5

Please sign in to comment.