summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDmitry Stogov <dmitry@zend.com>2019-06-24 20:32:27 +0300
committerDmitry Stogov <dmitry@zend.com>2019-06-24 20:32:27 +0300
commit0f29fb5cd81d9df4829bc63f704019df910b3075 (patch)
tree05c34dda76c2e45630d5fe9528d10dc5fef97bd3
parent94df6dc3fd07fa187ba09c587a8d1357fd8bbe6f (diff)
downloadphp-git-0f29fb5cd81d9df4829bc63f704019df910b3075.tar.gz
Fixed bug 78175 (Preloading must store default values of static variables and properties)
-rw-r--r--Zend/zend_compile.c15
-rw-r--r--Zend/zend_inheritance.c2
-rw-r--r--Zend/zend_object_handlers.c2
-rw-r--r--Zend/zend_opcode.c2
-rw-r--r--Zend/zend_vm_def.h2
-rw-r--r--Zend/zend_vm_execute.h2
-rw-r--r--ext/opcache/ZendAccelerator.c71
-rw-r--r--ext/opcache/tests/bug78175_2.phpt19
-rw-r--r--ext/opcache/tests/preload_bug78175_2.inc20
-rw-r--r--ext/opcache/zend_persist.c4
-rw-r--r--ext/opcache/zend_persist_calc.c4
11 files changed, 127 insertions, 16 deletions
diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c
index 0837c5b0b8..cb9419c7b8 100644
--- a/Zend/zend_compile.c
+++ b/Zend/zend_compile.c
@@ -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;
@@ -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;
diff --git a/Zend/zend_inheritance.c b/Zend/zend_inheritance.c
index ba1612c189..e62d990c18 100644
--- a/Zend/zend_inheritance.c
+++ b/Zend/zend_inheritance.c
@@ -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;
diff --git a/Zend/zend_object_handlers.c b/Zend/zend_object_handlers.c
index 10f5ac1524..3f8be6f707 100644
--- a/Zend/zend_object_handlers.c
+++ b/Zend/zend_object_handlers.c
@@ -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:
diff --git a/Zend/zend_opcode.c b/Zend/zend_opcode.c
index 6c8ff85b10..dd7c42a093 100644
--- a/Zend/zend_opcode.c
+++ b/Zend/zend_opcode.c
@@ -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) {
diff --git a/Zend/zend_vm_def.h b/Zend/zend_vm_def.h
index 330132b43d..3508cba5dd 100644
--- a/Zend/zend_vm_def.h
+++ b/Zend/zend_vm_def.h
@@ -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) {
diff --git a/Zend/zend_vm_execute.h b/Zend/zend_vm_execute.h
index 832f9bef42..b5e63d394f 100644
--- a/Zend/zend_vm_execute.h
+++ b/Zend/zend_vm_execute.h
@@ -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) {
diff --git a/ext/opcache/ZendAccelerator.c b/ext/opcache/ZendAccelerator.c
index c8f640bfeb..60e44c6508 100644
--- a/ext/opcache/ZendAccelerator.c
+++ b/ext/opcache/ZendAccelerator.c
@@ -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);
}
@@ -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);
}
@@ -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 {
@@ -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);
@@ -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;
@@ -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;
@@ -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();
@@ -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:
diff --git a/ext/opcache/tests/bug78175_2.phpt b/ext/opcache/tests/bug78175_2.phpt
new file mode 100644
index 0000000000..1d736f6e10
--- /dev/null
+++ b/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
diff --git a/ext/opcache/tests/preload_bug78175_2.inc b/ext/opcache/tests/preload_bug78175_2.inc
new file mode 100644
index 0000000000..288c142678
--- /dev/null
+++ b/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();
diff --git a/ext/opcache/zend_persist.c b/ext/opcache/zend_persist.c
index f5e1c45738..698611e1a7 100644
--- a/ext/opcache/zend_persist.c
+++ b/ext/opcache/zend_persist.c
@@ -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;
}
}
diff --git a/ext/opcache/zend_persist_calc.c b/ext/opcache/zend_persist_calc.c
index ca96da7ffa..a38398e3cf 100644
--- a/ext/opcache/zend_persist_calc.c
+++ b/ext/opcache/zend_persist_calc.c
@@ -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;
}
}