]> granicus.if.org Git - php/commitdiff
Fixed bug #74345
authorNikita Popov <nikita.ppv@gmail.com>
Wed, 20 Mar 2019 11:03:45 +0000 (12:03 +0100)
committerNikita Popov <nikita.ppv@gmail.com>
Wed, 20 Mar 2019 11:06:42 +0000 (12:06 +0100)
Export zend_release_fcall_info_cache(). It is only necessary to
call it if the fcc may not have been used -- if it is passed to
zend_call_function() and friends, then they will take care of
freeing trampolines.

Zend/zend_API.c
Zend/zend_API.h
ext/standard/array.c
ext/standard/tests/array/bug74345.phpt [new file with mode: 0644]

index 80a975252d6a6a4e3b5db9361abd9628efb51c71..881ee27a031e699a77a87c4044622952576b7383 100644 (file)
@@ -2907,7 +2907,7 @@ static int zend_is_callable_check_class(zend_string *name, zend_class_entry *sco
 }
 /* }}} */
 
-static void free_fcc(zend_fcall_info_cache *fcc) {
+ZEND_API void zend_release_fcall_info_cache(zend_fcall_info_cache *fcc) {
        if (fcc->function_handler &&
                ((fcc->function_handler->common.fn_flags & ZEND_ACC_CALL_VIA_TRAMPOLINE) ||
                 fcc->function_handler->type == ZEND_OVERLOADED_FUNCTION_TEMPORARY ||
@@ -2918,6 +2918,7 @@ static void free_fcc(zend_fcall_info_cache *fcc) {
                }
                zend_free_trampoline(fcc->function_handler);
        }
+       fcc->function_handler = NULL;
 }
 
 static zend_always_inline int zend_is_callable_check_func(int check_flags, zval *callable, zend_fcall_info_cache *fcc, int strict_class, char **error) /* {{{ */
@@ -3283,7 +3284,7 @@ again:
 check_func:
                        ret = zend_is_callable_check_func(check_flags, callable, fcc, strict_class, error);
                        if (fcc == &fcc_local) {
-                               free_fcc(fcc);
+                               zend_release_fcall_info_cache(fcc);
                        }
                        return ret;
 
@@ -3352,7 +3353,7 @@ check_func:
                        if (Z_OBJ_HANDLER_P(callable, get_closure) && Z_OBJ_HANDLER_P(callable, get_closure)(callable, &fcc->calling_scope, &fcc->function_handler, &fcc->object) == SUCCESS) {
                                fcc->called_scope = fcc->calling_scope;
                                if (fcc == &fcc_local) {
-                                       free_fcc(fcc);
+                                       zend_release_fcall_info_cache(fcc);
                                }
                                return 1;
                        }
@@ -3394,7 +3395,7 @@ ZEND_API zend_bool zend_make_callable(zval *callable, zend_string **callable_nam
                        add_next_index_str(callable, zend_string_copy(fcc.calling_scope->name));
                        add_next_index_str(callable, zend_string_copy(fcc.function_handler->common.function_name));
                }
-               free_fcc(&fcc);
+               zend_release_fcall_info_cache(&fcc);
                return 1;
        }
        return 0;
index 21ba965000077a029c349e564387419a59c91587..00a39f6624348bb976a94ce18d4858e2971da973 100644 (file)
@@ -302,6 +302,7 @@ ZEND_API ZEND_COLD void zend_wrong_param_count(void);
 
 #define IS_CALLABLE_STRICT  (IS_CALLABLE_CHECK_IS_STATIC)
 
+ZEND_API void zend_release_fcall_info_cache(zend_fcall_info_cache *fcc);
 ZEND_API zend_string *zend_get_callable_name_ex(zval *callable, zend_object *object);
 ZEND_API zend_string *zend_get_callable_name(zval *callable);
 ZEND_API zend_bool zend_is_callable_ex(zval *callable, zend_object *object, uint32_t check_flags, zend_string **callable_name, zend_fcall_info_cache *fcc, char **error);
index 297b0c0f380779bcd99e4a9549deae3a40e17d88..2658ad68f5a17bd53c8e2f5c8e78c98e830d3a13 100644 (file)
@@ -1013,6 +1013,7 @@ static int php_array_user_compare(const void *a, const void *b) /* {{{ */
        BG(user_compare_fci_cache) = empty_fcall_info_cache; \
 
 #define PHP_ARRAY_CMP_FUNC_RESTORE() \
+       zend_release_fcall_info_cache(&BG(user_compare_fci_cache)); \
        BG(user_compare_fci) = old_user_compare_fci; \
        BG(user_compare_fci_cache) = old_user_compare_fci_cache; \
 
@@ -1502,6 +1503,7 @@ PHP_FUNCTION(array_walk)
        );
 
        php_array_walk(array, userdata, 0);
+       zend_release_fcall_info_cache(&BG(array_walk_fci_cache));
        BG(array_walk_fci) = orig_array_walk_fci;
        BG(array_walk_fci_cache) = orig_array_walk_fci_cache;
        RETURN_TRUE;
@@ -1532,6 +1534,7 @@ PHP_FUNCTION(array_walk_recursive)
        );
 
        php_array_walk(array, userdata, 1);
+       zend_release_fcall_info_cache(&BG(array_walk_fci_cache));
        BG(array_walk_fci) = orig_array_walk_fci;
        BG(array_walk_fci_cache) = orig_array_walk_fci_cache;
        RETURN_TRUE;
@@ -6017,6 +6020,7 @@ PHP_FUNCTION(array_reduce)
 
        if (zend_hash_num_elements(htbl) == 0) {
                ZVAL_COPY_VALUE(return_value, &result);
+               zend_release_fcall_info_cache(&fci_cache);
                return;
        }
 
@@ -6040,6 +6044,7 @@ PHP_FUNCTION(array_reduce)
                }
        } ZEND_HASH_FOREACH_END();
 
+       zend_release_fcall_info_cache(&fci_cache);
        RETVAL_ZVAL(&result, 1, 1);
 }
 /* }}} */
@@ -6069,6 +6074,7 @@ PHP_FUNCTION(array_filter)
 
        array_init(return_value);
        if (zend_hash_num_elements(Z_ARRVAL_P(array)) == 0) {
+               zend_release_fcall_info_cache(&fci_cache);
                return;
        }
 
@@ -6130,6 +6136,8 @@ PHP_FUNCTION(array_filter)
                }
                zval_add_ref(operand);
        } ZEND_HASH_FOREACH_END();
+
+       zend_release_fcall_info_cache(&fci_cache);
 }
 /* }}} */
 
@@ -6167,6 +6175,7 @@ PHP_FUNCTION(array_map)
                /* Short-circuit: if no callback and only one array, just return it. */
                if (!ZEND_FCI_INITIALIZED(fci) || !maxlen) {
                        ZVAL_COPY(return_value, &arrays[0]);
+                       zend_release_fcall_info_cache(&fci_cache);
                        return;
                }
 
@@ -6192,6 +6201,8 @@ PHP_FUNCTION(array_map)
                                zend_hash_index_add_new(Z_ARRVAL_P(return_value), num_key, &result);
                        }
                } ZEND_HASH_FOREACH_END();
+
+               zend_release_fcall_info_cache(&fci_cache);
        } else {
                uint32_t *array_pos = (HashPosition *)ecalloc(n_arrays, sizeof(HashPosition));
 
@@ -6284,6 +6295,7 @@ PHP_FUNCTION(array_map)
                        }
 
                        efree(params);
+                       zend_release_fcall_info_cache(&fci_cache);
                }
                efree(array_pos);
        }
diff --git a/ext/standard/tests/array/bug74345.phpt b/ext/standard/tests/array/bug74345.phpt
new file mode 100644 (file)
index 0000000..144d257
--- /dev/null
@@ -0,0 +1,27 @@
+--TEST--
+Bug #74345: Call trampoline leaked if callback not invoked
+--FILE--
+<?php
+
+class Test {
+    public function __call($name, $args) {
+        echo "__call()\n";
+    }
+}
+
+$name = "foo" . ($x = "bar");
+$cb = [new Test, $name];
+array_map($cb, []);
+array_map($cb, [], []);
+array_filter([], $cb);
+array_reduce([], $cb);
+
+$array = [];
+array_walk($array, $cb);
+array_walk_recursive($array, $cb);
+usort($array, $cb);
+
+?>
+===DONE===
+--EXPECT--
+===DONE===