summaryrefslogtreecommitdiff
path: root/Zend/zend_compile.h
diff options
context:
space:
mode:
authorNikita Popov <nikita.ppv@gmail.com>2020-05-18 15:46:06 +0200
committerNikita Popov <nikita.ppv@gmail.com>2021-03-01 11:35:54 +0100
commit47a2e5c785cdba71e003d9ad77cb799d4be88806 (patch)
treed03767c644d6d6652a882b2b2946ff55a7a036a9 /Zend/zend_compile.h
parentb86dfb0e747a2254f3de97347ac89d791572141e (diff)
downloadphp-git-47a2e5c785cdba71e003d9ad77cb799d4be88806.tar.gz
Reference dynamic functions through dynamic_defs
Currently, dynamically declared functions and closures are inserted into the function table under a runtime definition key, and then later possibly renamed. When opcache is not used and a file containing a closure is repeatedly included, this leads to a very large memory leak, as the no longer needed closure declarations will never be freed (https://bugs.php.net/bug.php?id=76982). With this patch, dynamic functions are instead stored in a dynamic_func_defs member on the op_array, which opcodes reference by index. When the parent op_array is destroyed, the dynamic_func_defs it contains are also destroyed (unless they are stilled used elsewhere, e.g. because they have been bound, or are used by a live closure). This resolves the fundamental part of the leak, though doesn't completely fix it yet due to some arena allocations. The main non-obvious change here is to static variable handling: We can't destroy static_variables_ptr in destroy_op_array, as e.g. that would clear the static variables in a dynamic function when the op_array containing it is destroyed. Static variable destruction is separated out for this reason (we already do static variable destruction separately for normal functions, so we only need to handle main scripts). Closes GH-5595.
Diffstat (limited to 'Zend/zend_compile.h')
-rw-r--r--Zend/zend_compile.h8
1 files changed, 7 insertions, 1 deletions
diff --git a/Zend/zend_compile.h b/Zend/zend_compile.h
index ce4b50aaee..6f64624fd8 100644
--- a/Zend/zend_compile.h
+++ b/Zend/zend_compile.h
@@ -456,8 +456,13 @@ struct _zend_op_array {
zend_string *doc_comment;
int last_literal;
+ uint32_t num_dynamic_func_defs;
zval *literals;
+ /* Functions that are declared dynamically are stored here and
+ * referenced by index from opcodes. */
+ zend_op_array **dynamic_func_defs;
+
void *reserved[ZEND_MAX_RESERVED_RESOURCES];
};
@@ -781,7 +786,7 @@ bool zend_handle_encoding_declaration(zend_ast *ast);
/* parser-driven code generators */
void zend_do_free(znode *op1);
-ZEND_API zend_result do_bind_function(zval *lcname);
+ZEND_API zend_result do_bind_function(zend_function *func, zval *lcname);
ZEND_API zend_result do_bind_class(zval *lcname, zend_string *lc_parent_name);
ZEND_API uint32_t zend_build_delayed_early_binding_list(const zend_op_array *op_array);
ZEND_API void zend_do_delayed_early_binding(zend_op_array *op_array, uint32_t first_early_binding_opline);
@@ -812,6 +817,7 @@ ZEND_API int zend_execute_scripts(int type, zval *retval, int file_count, ...);
ZEND_API int open_file_for_scanning(zend_file_handle *file_handle);
ZEND_API void init_op_array(zend_op_array *op_array, zend_uchar type, int initial_ops_size);
ZEND_API void destroy_op_array(zend_op_array *op_array);
+ZEND_API void zend_destroy_static_vars(zend_op_array *op_array);
ZEND_API void zend_destroy_file_handle(zend_file_handle *file_handle);
ZEND_API void zend_cleanup_mutable_class_data(zend_class_entry *ce);
ZEND_API void zend_cleanup_internal_class_data(zend_class_entry *ce);