summaryrefslogtreecommitdiffstats
path: root/5492f345a2dbe2f2e0610e088a57283c25270e3b.patch
blob: 007a3d4bc11a1fc38c9fe8cde1e498456ed10e3e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
From a7584f4a86a84c4a440a28e76041058e4e072106 Mon Sep 17 00:00:00 2001
From: Yoshio HANAWA <y@hnw.jp>
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++;
 	}