]> granicus.if.org Git - php/commitdiff
Fixed bug 78175 (Preloading must store default values of static variables and properties)
authorDmitry Stogov <dmitry@zend.com>
Mon, 24 Jun 2019 17:32:27 +0000 (20:32 +0300)
committerDmitry Stogov <dmitry@zend.com>
Mon, 24 Jun 2019 17:32:27 +0000 (20:32 +0300)
Zend/zend_compile.c
Zend/zend_inheritance.c
Zend/zend_object_handlers.c
Zend/zend_opcode.c
Zend/zend_vm_def.h
Zend/zend_vm_execute.h
ext/opcache/ZendAccelerator.c
ext/opcache/tests/bug78175_2.phpt [new file with mode: 0644]
ext/opcache/tests/preload_bug78175_2.inc [new file with mode: 0644]
ext/opcache/zend_persist.c
ext/opcache/zend_persist_calc.c

index 0837c5b0b83ece040fc11381ec16c67d8d6f02ea..cb9419c7b8a0b7b2c7909f085485cbe3f7402b15 100644 (file)
@@ -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;
index ba1612c189c03a303230e4b8b6da2633c95b3dd0..e62d990c18ae1815ec407c0f01d08541e58337a9 100644 (file)
@@ -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;
index 10f5ac1524350d8de9e26d449530bb4d11a77e62..3f8be6f707c345c1223a673903d7285172ac0b11 100644 (file)
@@ -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:
index 6c8ff85b10c22f9ccc2023730b27b65036c39060..dd7c42a0937f8a4f6b0afc17ac472b1d577886b5 100644 (file)
@@ -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) {
index 330132b43dbfe3cdd3dab4772bf0b4d92d8e9865..3508cba5dd0b84b91c667f3810494d2432b02728 100644 (file)
@@ -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) {
index 832f9bef426154ffadbcdcbe044ed86ce988545c..b5e63d394f7530d996a3cf9fd3b0d04528a34896 100644 (file)
@@ -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) {
index c8f640bfeb2a9f065bddc117c7613f3a994d837c..60e44c6508cf5db01eeafe0ca4a84689ad62b91a 100644 (file)
@@ -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 (file)
index 0000000..1d736f6
--- /dev/null
@@ -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 (file)
index 0000000..288c142
--- /dev/null
@@ -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();
index f5e1c457383d8e043e6d8ed3a6a612ec0618be6d..698611e1a79096bbd39f7c1ae0cb588a0b27f2e5 100644 (file)
@@ -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;
        }
 }
 
index ca96da7ffa8ee99bcabbfe79a8a6dfa831b8166d..a38398e3cfac072270c6e80b19f31a0d7d1ee329 100644 (file)
@@ -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;
        }
 }