Skip to content

Commit 94b89ba

Browse files
committed
Properly handle static variables
1 parent 01d193f commit 94b89ba

File tree

8 files changed

+47
-10
lines changed

8 files changed

+47
-10
lines changed
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
--TEST--
2+
Static variables in dynamically declared function
3+
--FILE--
4+
<?php
5+
6+
$code = <<<'CODE'
7+
if (1) {
8+
function test() {
9+
static $x = 0;
10+
var_dump(++$x);
11+
}
12+
test();
13+
}
14+
CODE;
15+
eval($code);
16+
test();
17+
18+
?>
19+
--EXPECT--
20+
int(1)
21+
int(2)

Zend/zend.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1696,6 +1696,7 @@ ZEND_API zend_result zend_execute_scripts(int type, zval *retval, int file_count
16961696
ret = zend_exception_error(EG(exception), E_ERROR);
16971697
}
16981698
}
1699+
zend_destroy_static_vars(op_array);
16991700
destroy_op_array(op_array);
17001701
efree_size(op_array, sizeof(zend_op_array));
17011702
} else if (type==ZEND_REQUIRE) {

Zend/zend_closures.c

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ typedef struct _zend_closure {
3939
zval this_ptr;
4040
zend_class_entry *called_scope;
4141
zif_handler orig_internal_handler;
42+
HashTable *static_variables;
4243
} zend_closure;
4344

4445
/* non-static since it needs to be referenced */
@@ -486,10 +487,9 @@ static void zend_closure_free_storage(zend_object *object) /* {{{ */
486487
zend_object_std_dtor(&closure->std);
487488

488489
if (closure->func.type == ZEND_USER_FUNCTION) {
489-
/* We shared static_variables with the original function.
490-
* Unshare now so we don't try to destroy them. */
491-
if (closure->func.op_array.fn_flags & ZEND_ACC_FAKE_CLOSURE) {
492-
ZEND_MAP_PTR_INIT(closure->func.op_array.static_variables_ptr, NULL);
490+
/* We don't own the static variables of fake closures. */
491+
if (!(closure->func.op_array.fn_flags & ZEND_ACC_FAKE_CLOSURE)) {
492+
zend_destroy_static_vars(&closure->func.op_array);
493493
}
494494
destroy_op_array(&closure->func.op_array);
495495
}
@@ -689,11 +689,13 @@ static void zend_create_closure_ex(zval *res, zend_function *func, zend_class_en
689689
/* For fake closures, we want to reuse the static variables of the original function. */
690690
if (!is_fake) {
691691
if (closure->func.op_array.static_variables) {
692-
closure->func.op_array.static_variables =
693-
zend_array_dup(closure->func.op_array.static_variables);
692+
HashTable *static_variables =
693+
ZEND_MAP_PTR_GET(closure->func.op_array.static_variables_ptr);
694+
closure->static_variables = zend_array_dup(
695+
static_variables ? static_variables : closure->func.op_array.static_variables);
694696
}
695697
ZEND_MAP_PTR_INIT(closure->func.op_array.static_variables_ptr,
696-
&closure->func.op_array.static_variables);
698+
&closure->static_variables);
697699
}
698700

699701
/* Runtime cache is scope-dependent, so we cannot reuse it if the scope changed */

Zend/zend_compile.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -817,6 +817,7 @@ ZEND_API int zend_execute_scripts(int type, zval *retval, int file_count, ...);
817817
ZEND_API int open_file_for_scanning(zend_file_handle *file_handle);
818818
ZEND_API void init_op_array(zend_op_array *op_array, zend_uchar type, int initial_ops_size);
819819
ZEND_API void destroy_op_array(zend_op_array *op_array);
820+
ZEND_API void zend_destroy_static_vars(zend_op_array *op_array);
820821
ZEND_API void zend_destroy_file_handle(zend_file_handle *file_handle);
821822
ZEND_API void zend_cleanup_mutable_class_data(zend_class_entry *ce);
822823
ZEND_API void zend_cleanup_internal_class_data(zend_class_entry *ce);

Zend/zend_execute_API.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1224,6 +1224,7 @@ ZEND_API zend_result zend_eval_stringl(const char *str, size_t str_len, zval *re
12241224
}
12251225

12261226
EG(no_extensions)=0;
1227+
zend_destroy_static_vars(new_op_array);
12271228
destroy_op_array(new_op_array);
12281229
efree_size(new_op_array, sizeof(zend_op_array));
12291230
retval = SUCCESS;

Zend/zend_opcode.c

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -514,17 +514,20 @@ void zend_class_add_ref(zval *zv)
514514
}
515515
}
516516

517-
ZEND_API void destroy_op_array(zend_op_array *op_array)
517+
ZEND_API void zend_destroy_static_vars(zend_op_array *op_array)
518518
{
519-
uint32_t i;
520-
521519
if (ZEND_MAP_PTR(op_array->static_variables_ptr)) {
522520
HashTable *ht = ZEND_MAP_PTR_GET(op_array->static_variables_ptr);
523521
if (ht) {
524522
zend_array_destroy(ht);
525523
ZEND_MAP_PTR_SET(op_array->static_variables_ptr, NULL);
526524
}
527525
}
526+
}
527+
528+
ZEND_API void destroy_op_array(zend_op_array *op_array)
529+
{
530+
uint32_t i;
528531

529532
if ((op_array->fn_flags & ZEND_ACC_HEAP_RT_CACHE)
530533
&& ZEND_MAP_PTR(op_array->run_time_cache)) {

Zend/zend_vm_def.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2824,6 +2824,7 @@ ZEND_VM_HOT_HELPER(zend_leave_helper, ANY, ANY)
28242824
ZEND_VM_LEAVE();
28252825
} else if (EXPECTED((call_info & ZEND_CALL_TOP) == 0)) {
28262826
zend_detach_symbol_table(execute_data);
2827+
zend_destroy_static_vars(&EX(func)->op_array);
28272828
destroy_op_array(&EX(func)->op_array);
28282829
efree_size(EX(func), sizeof(zend_op_array));
28292830
#ifdef ZEND_PREFER_RELOAD
@@ -6231,6 +6232,7 @@ ZEND_VM_HANDLER(73, ZEND_INCLUDE_OR_EVAL, CONST|TMPVAR|CV, ANY, EVAL, SPEC(OBSER
62316232
zend_vm_stack_free_call_frame(call);
62326233
}
62336234

6235+
zend_destroy_static_vars(new_op_array);
62346236
destroy_op_array(new_op_array);
62356237
efree_size(new_op_array, sizeof(zend_op_array));
62366238
if (UNEXPECTED(EG(exception) != NULL)) {

Zend/zend_vm_execute.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1146,6 +1146,7 @@ static zend_never_inline ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_leave_helper
11461146
ZEND_VM_LEAVE();
11471147
} else if (EXPECTED((call_info & ZEND_CALL_TOP) == 0)) {
11481148
zend_detach_symbol_table(execute_data);
1149+
zend_destroy_static_vars(&EX(func)->op_array);
11491150
destroy_op_array(&EX(func)->op_array);
11501151
efree_size(EX(func), sizeof(zend_op_array));
11511152
#ifdef ZEND_PREFER_RELOAD
@@ -4734,6 +4735,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INCLUDE_OR_EVAL_SPEC_CONST_HAN
47344735
zend_vm_stack_free_call_frame(call);
47354736
}
47364737

4738+
zend_destroy_static_vars(new_op_array);
47374739
destroy_op_array(new_op_array);
47384740
efree_size(new_op_array, sizeof(zend_op_array));
47394741
if (UNEXPECTED(EG(exception) != NULL)) {
@@ -4803,6 +4805,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INCLUDE_OR_EVAL_SPEC_OBSERVER_
48034805
zend_vm_stack_free_call_frame(call);
48044806
}
48054807

4808+
zend_destroy_static_vars(new_op_array);
48064809
destroy_op_array(new_op_array);
48074810
efree_size(new_op_array, sizeof(zend_op_array));
48084811
if (UNEXPECTED(EG(exception) != NULL)) {
@@ -14349,6 +14352,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INCLUDE_OR_EVAL_SPEC_TMPVAR_HA
1434914352
zend_vm_stack_free_call_frame(call);
1435014353
}
1435114354

14355+
zend_destroy_static_vars(new_op_array);
1435214356
destroy_op_array(new_op_array);
1435314357
efree_size(new_op_array, sizeof(zend_op_array));
1435414358
if (UNEXPECTED(EG(exception) != NULL)) {
@@ -38232,6 +38236,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INCLUDE_OR_EVAL_SPEC_CV_HANDLE
3823238236
zend_vm_stack_free_call_frame(call);
3823338237
}
3823438238

38239+
zend_destroy_static_vars(new_op_array);
3823538240
destroy_op_array(new_op_array);
3823638241
efree_size(new_op_array, sizeof(zend_op_array));
3823738242
if (UNEXPECTED(EG(exception) != NULL)) {
@@ -54618,6 +54623,7 @@ ZEND_API void execute_ex(zend_execute_data *ex)
5461854623
ZEND_VM_LEAVE();
5461954624
} else if (EXPECTED((call_info & ZEND_CALL_TOP) == 0)) {
5462054625
zend_detach_symbol_table(execute_data);
54626+
zend_destroy_static_vars(&EX(func)->op_array);
5462154627
destroy_op_array(&EX(func)->op_array);
5462254628
efree_size(EX(func), sizeof(zend_op_array));
5462354629
#ifdef ZEND_PREFER_RELOAD

0 commit comments

Comments
 (0)