From a7584f4a86a84c4a440a28e76041058e4e072106 Mon Sep 17 00:00:00 2001 From: Yoshio HANAWA Date: Sun, 19 Nov 2017 20:30:50 +0900 Subject: [PATCH] Fix "double free" bug against override function caused by function destructor on PHP 7.2.0+ --- php_timecop.h | 18 ++++++++++++++++++ timecop_php7.c | 14 +++++++++++--- 2 files changed, 29 insertions(+), 3 deletions(-) diff --git a/php_timecop.h b/php_timecop.h index 6606ed0..14e8d43 100644 --- a/php_timecop.h +++ b/php_timecop.h @@ -148,6 +148,24 @@ ZEND_END_MODULE_GLOBALS(timecop) #define TIMECOP_OCE(cname, mname) \ {cname, mname, OVRD_CLASS_PREFIX cname, SAVE_FUNC_PREFIX mname} +/* + * Trick for guarding the multi-referenced internal function from function destructor on PHP 7.2.0+ + * See: https://github.com/hnw/php-timecop/issues/29#issuecomment-332171527 + */ +#define GUARD_FUNCTION_ARG_INFO_BEGIN(zend_func) { \ + zend_arg_info *orig_arg_info; \ + zend_function *zf = zend_func; \ + if (zf->type == ZEND_INTERNAL_FUNCTION) { \ + orig_arg_info = zf->common.arg_info; \ + zf->common.arg_info = NULL; \ + } + +#define GUARD_FUNCTION_ARG_INFO_END() \ + if (zf->type == ZEND_INTERNAL_FUNCTION) { \ + zf->common.arg_info = orig_arg_info; \ + } \ +} + struct timecop_override_func_entry { char *orig_func; char *ovrd_func; diff --git a/timecop_php7.c b/timecop_php7.c index b4d01e7..4acbc57 100644 --- a/timecop_php7.c +++ b/timecop_php7.c @@ -189,7 +189,7 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_timecop_date_create_from_format, 0, 0, 2) ZEND_ARG_INFO(0, format) ZEND_ARG_INFO(0, time) #if PHP_VERSION_ID >= 70200 - ZEND_ARG_OBJ_INFO(0, object, DateTimeZone, 1) + ZEND_ARG_OBJ_INFO(0, object, DateTimeZone, 1) #else ZEND_ARG_INFO(0, object) #endif @@ -527,8 +527,10 @@ static int timecop_func_override() zf_orig, sizeof(zend_internal_function)); function_add_ref(zf_orig); + GUARD_FUNCTION_ARG_INFO_BEGIN(zf_orig); zend_hash_str_update_mem(EG(function_table), p->orig_func, strlen(p->orig_func), zf_ovrd, sizeof(zend_internal_function)); + GUARD_FUNCTION_ARG_INFO_END(); function_add_ref(zf_ovrd); p++; @@ -620,22 +622,28 @@ static int timecop_class_override() static int timecop_func_override_clear() { const struct timecop_override_func_entry *p; - zend_function *zf_orig; + zend_function *zf_orig, *zf_ovld; p = &(timecop_override_func_table[0]); while (p->orig_func != NULL) { zf_orig = zend_hash_str_find_ptr(EG(function_table), p->save_func, strlen(p->save_func)); - if (zf_orig == NULL) { + zf_ovld = zend_hash_str_find_ptr(EG(function_table), + p->orig_func, strlen(p->orig_func)); + if (zf_orig == NULL || zf_ovld == NULL) { p++; continue; } + GUARD_FUNCTION_ARG_INFO_BEGIN(zf_ovld); zend_hash_str_update_mem(EG(function_table), p->orig_func, strlen(p->orig_func), zf_orig, sizeof(zend_internal_function)); + GUARD_FUNCTION_ARG_INFO_END(); function_add_ref(zf_orig); + GUARD_FUNCTION_ARG_INFO_BEGIN(zf_orig); zend_hash_str_del(EG(function_table), p->save_func, strlen(p->save_func)); + GUARD_FUNCTION_ARG_INFO_END(); p++; }