1 /*
2   +----------------------------------------------------------------------+
3   | Copyright (c) The PHP Group                                          |
4   +----------------------------------------------------------------------+
5   | This source file is subject to version 3.01 of the PHP license,      |
6   | that is bundled with this package in the file LICENSE, and is        |
7   | available through the world-wide-web at the following url:           |
8   | https://www.php.net/license/3_01.txt                                 |
9   | If you did not receive a copy of the PHP license and are unable to   |
10   | obtain it through the world-wide-web, please send a note to          |
11   | license@php.net so we can mail you a copy immediately.               |
12   +----------------------------------------------------------------------+
13   | Author: Florian Engelhardt <florian@engelhardt.tc>                   |
14   +----------------------------------------------------------------------+
15 */
16 
17 #include "php.h"
18 #include "php_test.h"
19 #include "Zend/zend_alloc.h"
20 #include "zend_portability.h"
21 #include <stddef.h>
22 #include <stdio.h>
23 
observe_malloc(size_t size ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC)24 void* observe_malloc(size_t size ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC)
25 {
26 	void *ptr = NULL;
27 	if (ZT_G(custom_malloc)) {
28 		zend_mm_set_heap(ZT_G(original_heap));
29 		ptr = ZT_G(custom_malloc)(size ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC);
30 		zend_mm_set_heap(ZT_G(observed_heap));
31 	} else {
32 		ptr = _zend_mm_alloc(ZT_G(original_heap), size ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC);
33 	}
34 	printf("Allocated %zu bytes at %p\n", size, ptr);
35 	return ptr;
36 }
37 
observe_free(void * ptr ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC)38 void observe_free(void* ptr ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC)
39 {
40 	if (ZT_G(custom_free))
41 	{
42 		zend_mm_set_heap(ZT_G(original_heap));
43 		ZT_G(custom_free)(ptr ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC);
44 		zend_mm_set_heap(ZT_G(observed_heap));
45 	} else {
46 		_zend_mm_free(ZT_G(original_heap), ptr ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC);
47 	}
48 	printf("Freed memory at %p\n", ptr);
49 }
50 
observe_realloc(void * ptr,size_t size ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC)51 void* observe_realloc(void* ptr, size_t size ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC)
52 {
53 	void * new_ptr;
54 	if (ZT_G(custom_realloc))
55 	{
56 		zend_mm_set_heap(ZT_G(original_heap));
57 		new_ptr = ZT_G(custom_realloc)(ptr, size ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC);
58 		zend_mm_set_heap(ZT_G(observed_heap));
59 	} else {
60 		new_ptr = _zend_mm_realloc(ZT_G(original_heap), ptr, size ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC);
61 	}
62 	printf("Realloc of %zu bytes from %p to %p\n", size, ptr, new_ptr);
63 	return new_ptr;
64 }
65 
observe_gc(void)66 size_t observe_gc(void)
67 {
68 	size_t size = 0;
69 	if (ZT_G(custom_gc)) {
70 		zend_mm_set_heap(ZT_G(original_heap));
71 		size = ZT_G(custom_gc)();
72 		zend_mm_set_heap(ZT_G(observed_heap));
73 	} else {
74 		size = zend_mm_gc(ZT_G(original_heap));
75 	}
76 	printf("ZendMM GC freed %zu bytes", size);
77 	return size;
78 }
79 
observe_shutdown(bool full,bool silent)80 void observe_shutdown(bool full, bool silent)
81 {
82 	if (ZT_G(custom_shutdown)) {
83 		zend_mm_set_heap(ZT_G(original_heap));
84 		ZT_G(custom_shutdown)(full, silent);
85 		zend_mm_set_heap(ZT_G(observed_heap));
86 	} else {
87 		zend_mm_shutdown(ZT_G(original_heap), full, silent);
88 	}
89 	printf("Shutdown happened: full -> %d, silent -> %d\n", full, silent);
90 }
91 
zend_test_mm_custom_handlers_init(void)92 void zend_test_mm_custom_handlers_init(void)
93 {
94 	if (ZT_G(observed_heap) != NULL) {
95 		return;
96 	}
97 	ZT_G(original_heap) = zend_mm_get_heap();
98 	if (zend_mm_is_custom_heap(ZT_G(original_heap))) {
99 		zend_mm_get_custom_handlers_ex(
100 			ZT_G(original_heap),
101 			&ZT_G(custom_malloc),
102 			&ZT_G(custom_free),
103 			&ZT_G(custom_realloc),
104 			&ZT_G(custom_gc),
105 			&ZT_G(custom_shutdown)
106 		);
107 	}
108 	printf("Prev handlers at %p, %p, %p, %p, %p\n", ZT_G(custom_malloc), ZT_G(custom_free), ZT_G(custom_realloc), ZT_G(custom_gc), ZT_G(custom_shutdown));
109 	ZT_G(observed_heap) = zend_mm_startup();
110 	zend_mm_set_custom_handlers_ex(
111 		ZT_G(observed_heap),
112 		observe_malloc,
113 		observe_free,
114 		observe_realloc,
115 		observe_gc,
116 		observe_shutdown
117 	);
118 	zend_mm_set_heap(ZT_G(observed_heap));
119 	printf("Heap at %p installed in ZendMM (orig at %p)\n", ZT_G(observed_heap), ZT_G(original_heap));
120 }
121 
zend_test_mm_custom_handlers_shutdown(void)122 void zend_test_mm_custom_handlers_shutdown(void)
123 {
124 	if (ZT_G(observed_heap) == NULL) {
125 		return;
126 	}
127 	zend_mm_set_heap(ZT_G(original_heap));
128 	zend_mm_set_custom_handlers_ex(
129 		ZT_G(observed_heap),
130 		NULL,
131 		NULL,
132 		NULL,
133 		NULL,
134 		NULL
135 	);
136 	zend_mm_shutdown(ZT_G(observed_heap), true, true);
137 	ZT_G(observed_heap) = NULL;
138 	printf("Prev heap at %p restored in ZendMM\n", ZT_G(original_heap));
139 }
140 
zend_test_mm_custom_handlers_rshutdown(void)141 void zend_test_mm_custom_handlers_rshutdown(void)
142 {
143 	zend_test_mm_custom_handlers_shutdown();
144 }
145 
zend_test_mm_custom_handlers_rinit(void)146 void zend_test_mm_custom_handlers_rinit(void)
147 {
148 	if (ZT_G(zend_mm_custom_handlers_enabled)) {
149 		zend_test_mm_custom_handlers_init();
150 	}
151 }
152 
PHP_INI_MH(OnUpdateZendTestMMCustomHandlersEnabled)153 static PHP_INI_MH(OnUpdateZendTestMMCustomHandlersEnabled)
154 {
155 	if (new_value == NULL) {
156 		return FAILURE;
157 	}
158 
159 	int int_value = zend_ini_parse_bool(new_value);
160 
161 	if (int_value == 1) {
162 		zend_test_mm_custom_handlers_init();
163 	} else {
164 		zend_test_mm_custom_handlers_shutdown();
165 	}
166 	return OnUpdateBool(entry, new_value, mh_arg1, mh_arg2, mh_arg3, stage);
167 }
168 
169 PHP_INI_BEGIN()
170 	STD_PHP_INI_BOOLEAN("zend_test.zend_mm_custom_handlers.enabled", "0", PHP_INI_USER, OnUpdateZendTestMMCustomHandlersEnabled, zend_mm_custom_handlers_enabled, zend_zend_test_globals, zend_test_globals)
PHP_INI_END()171 PHP_INI_END()
172 
173 void zend_test_mm_custom_handlers_minit(INIT_FUNC_ARGS)
174 {
175 	if (type != MODULE_TEMPORARY) {
176 		REGISTER_INI_ENTRIES();
177 	} else {
178 		(void)ini_entries;
179 	}
180 }
181