/src/php-src/Zend/zend_extensions.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | +----------------------------------------------------------------------+ |
3 | | | Zend Engine | |
4 | | +----------------------------------------------------------------------+ |
5 | | | Copyright (c) Zend Technologies Ltd. (http://www.zend.com) | |
6 | | +----------------------------------------------------------------------+ |
7 | | | This source file is subject to version 2.00 of the Zend license, | |
8 | | | that is bundled with this package in the file LICENSE, and is | |
9 | | | available through the world-wide-web at the following url: | |
10 | | | http://www.zend.com/license/2_00.txt. | |
11 | | | If you did not receive a copy of the Zend license and are unable to | |
12 | | | obtain it through the world-wide-web, please send a note to | |
13 | | | license@zend.com so we can mail you a copy immediately. | |
14 | | +----------------------------------------------------------------------+ |
15 | | | Authors: Andi Gutmans <andi@php.net> | |
16 | | | Zeev Suraski <zeev@php.net> | |
17 | | +----------------------------------------------------------------------+ |
18 | | */ |
19 | | |
20 | | #include "zend_extensions.h" |
21 | | #include "zend_system_id.h" |
22 | | |
23 | | ZEND_API zend_llist zend_extensions; |
24 | | ZEND_API uint32_t zend_extension_flags = 0; |
25 | | ZEND_API int zend_op_array_extension_handles = 0; |
26 | | ZEND_API int zend_internal_function_extension_handles = 0; |
27 | | static int last_resource_number; |
28 | | |
29 | | zend_result zend_load_extension(const char *path) |
30 | 4 | { |
31 | 4 | #if ZEND_EXTENSIONS_SUPPORT |
32 | 4 | DL_HANDLE handle; |
33 | | |
34 | 4 | handle = DL_LOAD(path); |
35 | 4 | if (!handle) { |
36 | 0 | #ifndef ZEND_WIN32 |
37 | 0 | fprintf(stderr, "Failed loading %s: %s\n", path, DL_ERROR()); |
38 | | #else |
39 | | fprintf(stderr, "Failed loading %s\n", path); |
40 | | /* See http://support.microsoft.com/kb/190351 */ |
41 | | fflush(stderr); |
42 | | #endif |
43 | 0 | return FAILURE; |
44 | 0 | } |
45 | | #ifdef ZEND_WIN32 |
46 | | char *err; |
47 | | if (!php_win32_image_compatible(handle, &err)) { |
48 | | zend_error(E_CORE_WARNING, "%s", err); |
49 | | return FAILURE; |
50 | | } |
51 | | #endif |
52 | 4 | return zend_load_extension_handle(handle, path); |
53 | | #else |
54 | | fprintf(stderr, "Extensions are not supported on this platform.\n"); |
55 | | /* See http://support.microsoft.com/kb/190351 */ |
56 | | #ifdef ZEND_WIN32 |
57 | | fflush(stderr); |
58 | | #endif |
59 | | return FAILURE; |
60 | | #endif |
61 | 4 | } |
62 | | |
63 | | zend_result zend_load_extension_handle(DL_HANDLE handle, const char *path) |
64 | 4 | { |
65 | 4 | #if ZEND_EXTENSIONS_SUPPORT |
66 | 4 | zend_extension *new_extension; |
67 | | |
68 | 4 | const zend_extension_version_info *extension_version_info = (const zend_extension_version_info *) DL_FETCH_SYMBOL(handle, "extension_version_info"); |
69 | 4 | if (!extension_version_info) { |
70 | 0 | extension_version_info = (const zend_extension_version_info *) DL_FETCH_SYMBOL(handle, "_extension_version_info"); |
71 | 0 | } |
72 | 4 | new_extension = (zend_extension *) DL_FETCH_SYMBOL(handle, "zend_extension_entry"); |
73 | 4 | if (!new_extension) { |
74 | 0 | new_extension = (zend_extension *) DL_FETCH_SYMBOL(handle, "_zend_extension_entry"); |
75 | 0 | } |
76 | 4 | if (!extension_version_info || !new_extension) { |
77 | 0 | fprintf(stderr, "%s doesn't appear to be a valid Zend extension\n", path); |
78 | | /* See http://support.microsoft.com/kb/190351 */ |
79 | | #ifdef ZEND_WIN32 |
80 | | fflush(stderr); |
81 | | #endif |
82 | 0 | DL_UNLOAD(handle); |
83 | 0 | return FAILURE; |
84 | 0 | } |
85 | | |
86 | | /* allow extension to proclaim compatibility with any Zend version */ |
87 | 4 | if (extension_version_info->zend_extension_api_no != ZEND_EXTENSION_API_NO &&(!new_extension->api_no_check || new_extension->api_no_check(ZEND_EXTENSION_API_NO) != SUCCESS)) { |
88 | 0 | if (extension_version_info->zend_extension_api_no > ZEND_EXTENSION_API_NO) { |
89 | 0 | fprintf(stderr, "%s requires Zend Engine API version %d.\n" |
90 | 0 | "The Zend Engine API version %d which is installed, is outdated.\n\n", |
91 | 0 | new_extension->name, |
92 | 0 | extension_version_info->zend_extension_api_no, |
93 | 0 | ZEND_EXTENSION_API_NO); |
94 | | /* See http://support.microsoft.com/kb/190351 */ |
95 | | #ifdef ZEND_WIN32 |
96 | | fflush(stderr); |
97 | | #endif |
98 | 0 | DL_UNLOAD(handle); |
99 | 0 | return FAILURE; |
100 | 0 | } else if (extension_version_info->zend_extension_api_no < ZEND_EXTENSION_API_NO) { |
101 | 0 | fprintf(stderr, "%s requires Zend Engine API version %d.\n" |
102 | 0 | "The Zend Engine API version %d which is installed, is newer.\n" |
103 | 0 | "Contact %s at %s for a later version of %s.\n\n", |
104 | 0 | new_extension->name, |
105 | 0 | extension_version_info->zend_extension_api_no, |
106 | 0 | ZEND_EXTENSION_API_NO, |
107 | 0 | new_extension->author, |
108 | 0 | new_extension->URL, |
109 | 0 | new_extension->name); |
110 | | /* See http://support.microsoft.com/kb/190351 */ |
111 | | #ifdef ZEND_WIN32 |
112 | | fflush(stderr); |
113 | | #endif |
114 | 0 | DL_UNLOAD(handle); |
115 | 0 | return FAILURE; |
116 | 0 | } |
117 | 4 | } else if (strcmp(ZEND_EXTENSION_BUILD_ID, extension_version_info->build_id) && |
118 | 4 | (!new_extension->build_id_check || new_extension->build_id_check(ZEND_EXTENSION_BUILD_ID) != SUCCESS)) { |
119 | 0 | fprintf(stderr, "Cannot load %s - it was built with configuration %s, whereas running engine is %s\n", |
120 | 0 | new_extension->name, extension_version_info->build_id, ZEND_EXTENSION_BUILD_ID); |
121 | | /* See http://support.microsoft.com/kb/190351 */ |
122 | | #ifdef ZEND_WIN32 |
123 | | fflush(stderr); |
124 | | #endif |
125 | 0 | DL_UNLOAD(handle); |
126 | 0 | return FAILURE; |
127 | 4 | } else if (zend_get_extension(new_extension->name)) { |
128 | 0 | fprintf(stderr, "Cannot load %s - it was already loaded\n", new_extension->name); |
129 | | /* See http://support.microsoft.com/kb/190351 */ |
130 | | #ifdef ZEND_WIN32 |
131 | | fflush(stderr); |
132 | | #endif |
133 | 0 | DL_UNLOAD(handle); |
134 | 0 | return FAILURE; |
135 | 0 | } |
136 | | |
137 | 4 | zend_register_extension(new_extension, handle); |
138 | 4 | return SUCCESS; |
139 | | #else |
140 | | fprintf(stderr, "Extensions are not supported on this platform.\n"); |
141 | | /* See http://support.microsoft.com/kb/190351 */ |
142 | | #ifdef ZEND_WIN32 |
143 | | fflush(stderr); |
144 | | #endif |
145 | | return FAILURE; |
146 | | #endif |
147 | 4 | } |
148 | | |
149 | | |
150 | | void zend_register_extension(zend_extension *new_extension, DL_HANDLE handle) |
151 | 4 | { |
152 | 4 | #if ZEND_EXTENSIONS_SUPPORT |
153 | 4 | zend_extension extension; |
154 | | |
155 | 4 | extension = *new_extension; |
156 | 4 | extension.handle = handle; |
157 | | |
158 | 4 | zend_extension_dispatch_message(ZEND_EXTMSG_NEW_EXTENSION, &extension); |
159 | | |
160 | 4 | zend_llist_add_element(&zend_extensions, &extension); |
161 | | |
162 | 4 | if (extension.op_array_ctor) { |
163 | 0 | zend_extension_flags |= ZEND_EXTENSIONS_HAVE_OP_ARRAY_CTOR; |
164 | 0 | } |
165 | 4 | if (extension.op_array_dtor) { |
166 | 0 | zend_extension_flags |= ZEND_EXTENSIONS_HAVE_OP_ARRAY_DTOR; |
167 | 0 | } |
168 | 4 | if (extension.op_array_handler) { |
169 | 0 | zend_extension_flags |= ZEND_EXTENSIONS_HAVE_OP_ARRAY_HANDLER; |
170 | 0 | } |
171 | 4 | if (extension.op_array_persist_calc) { |
172 | 0 | zend_extension_flags |= ZEND_EXTENSIONS_HAVE_OP_ARRAY_PERSIST_CALC; |
173 | 0 | } |
174 | 4 | if (extension.op_array_persist) { |
175 | 0 | zend_extension_flags |= ZEND_EXTENSIONS_HAVE_OP_ARRAY_PERSIST; |
176 | 0 | } |
177 | | /*fprintf(stderr, "Loaded %s, version %s\n", extension.name, extension.version);*/ |
178 | 4 | #endif |
179 | 4 | } |
180 | | |
181 | | |
182 | | static void zend_extension_shutdown(zend_extension *extension) |
183 | 0 | { |
184 | 0 | #if ZEND_EXTENSIONS_SUPPORT |
185 | 0 | if (extension->shutdown) { |
186 | 0 | extension->shutdown(extension); |
187 | 0 | } |
188 | 0 | #endif |
189 | 0 | } |
190 | | |
191 | | /* int return due to zend linked list API */ |
192 | | static int zend_extension_startup(zend_extension *extension) |
193 | 4 | { |
194 | 4 | #if ZEND_EXTENSIONS_SUPPORT |
195 | 4 | if (extension->startup) { |
196 | 4 | if (extension->startup(extension)!=SUCCESS) { |
197 | 0 | return 1; |
198 | 0 | } |
199 | 4 | zend_append_version_info(extension); |
200 | 4 | } |
201 | 4 | #endif |
202 | 4 | return 0; |
203 | 4 | } |
204 | | |
205 | | |
206 | | void zend_startup_extensions_mechanism(void) |
207 | 16 | { |
208 | | /* Startup extensions mechanism */ |
209 | 16 | zend_llist_init(&zend_extensions, sizeof(zend_extension), (void (*)(void *)) zend_extension_dtor, 1); |
210 | 16 | zend_op_array_extension_handles = 0; |
211 | 16 | zend_internal_function_extension_handles = 0; |
212 | 16 | last_resource_number = 0; |
213 | 16 | } |
214 | | |
215 | | |
216 | | void zend_startup_extensions(void) |
217 | 16 | { |
218 | 16 | zend_llist_apply_with_del(&zend_extensions, (int (*)(void *)) zend_extension_startup); |
219 | 16 | } |
220 | | |
221 | | |
222 | | void zend_shutdown_extensions(void) |
223 | 0 | { |
224 | 0 | zend_llist_apply(&zend_extensions, (llist_apply_func_t) zend_extension_shutdown); |
225 | 0 | zend_llist_destroy(&zend_extensions); |
226 | 0 | } |
227 | | |
228 | | |
229 | | void zend_extension_dtor(zend_extension *extension) |
230 | 0 | { |
231 | | #if ZEND_EXTENSIONS_SUPPORT && !ZEND_DEBUG |
232 | | if (extension->handle && !getenv("ZEND_DONT_UNLOAD_MODULES")) { |
233 | | DL_UNLOAD(extension->handle); |
234 | | } |
235 | | #endif |
236 | 0 | } |
237 | | |
238 | | |
239 | | static void zend_extension_message_dispatcher(const zend_extension *extension, int num_args, va_list args) |
240 | 0 | { |
241 | 0 | int message; |
242 | 0 | void *arg; |
243 | |
|
244 | 0 | if (!extension->message_handler || num_args!=2) { |
245 | 0 | return; |
246 | 0 | } |
247 | 0 | message = va_arg(args, int); |
248 | 0 | arg = va_arg(args, void *); |
249 | 0 | extension->message_handler(message, arg); |
250 | 0 | } |
251 | | |
252 | | |
253 | | ZEND_API void zend_extension_dispatch_message(int message, void *arg) |
254 | 4 | { |
255 | 4 | zend_llist_apply_with_arguments(&zend_extensions, (llist_apply_with_args_func_t) zend_extension_message_dispatcher, 2, message, arg); |
256 | 4 | } |
257 | | |
258 | | |
259 | | ZEND_API int zend_get_resource_handle(const char *module_name) |
260 | 16 | { |
261 | 16 | if (last_resource_number<ZEND_MAX_RESERVED_RESOURCES) { |
262 | 16 | zend_add_system_entropy(module_name, "zend_get_resource_handle", &last_resource_number, sizeof(int)); |
263 | 16 | return last_resource_number++; |
264 | 16 | } else { |
265 | 0 | return -1; |
266 | 0 | } |
267 | 16 | } |
268 | | |
269 | | /** |
270 | | * The handle returned by this function can be used with |
271 | | * `ZEND_OP_ARRAY_EXTENSION(op_array, handle)`. |
272 | | * |
273 | | * The extension slot has been available since PHP 7.4 on user functions and |
274 | | * has been available since PHP 8.2 on internal functions. |
275 | | * |
276 | | * # Safety |
277 | | * The extension slot made available by calling this function is initialized on |
278 | | * the first call made to the function in that request. If you need to |
279 | | * initialize it before this point, call `zend_init_func_run_time_cache`. |
280 | | * |
281 | | * The function cache slots are not available if the function is a trampoline, |
282 | | * which can be checked with something like: |
283 | | * |
284 | | * if (fbc->type == ZEND_USER_FUNCTION |
285 | | * && !(fbc->op_array.fn_flags & ZEND_ACC_CALL_VIA_TRAMPOLINE) |
286 | | * ) { |
287 | | * // Use ZEND_OP_ARRAY_EXTENSION somehow |
288 | | * } |
289 | | */ |
290 | | ZEND_API int zend_get_op_array_extension_handle(const char *module_name) |
291 | 0 | { |
292 | 0 | int handle = zend_op_array_extension_handles++; |
293 | 0 | zend_add_system_entropy(module_name, "zend_get_op_array_extension_handle", &zend_op_array_extension_handles, sizeof(int)); |
294 | 0 | return handle; |
295 | 0 | } |
296 | | |
297 | | /** See zend_get_op_array_extension_handle for important usage information. */ |
298 | | ZEND_API int zend_get_op_array_extension_handles(const char *module_name, int handles) |
299 | 0 | { |
300 | 0 | int handle = zend_op_array_extension_handles; |
301 | 0 | zend_op_array_extension_handles += handles; |
302 | 0 | zend_add_system_entropy(module_name, "zend_get_op_array_extension_handle", &zend_op_array_extension_handles, sizeof(int)); |
303 | 0 | return handle; |
304 | 0 | } |
305 | | |
306 | | ZEND_API int zend_get_internal_function_extension_handle(const char *module_name) |
307 | 0 | { |
308 | 0 | int handle = zend_internal_function_extension_handles++; |
309 | 0 | zend_add_system_entropy(module_name, "zend_get_internal_function_extension_handle", &zend_internal_function_extension_handles, sizeof(int)); |
310 | 0 | return handle; |
311 | 0 | } |
312 | | |
313 | | ZEND_API int zend_get_internal_function_extension_handles(const char *module_name, int handles) |
314 | 0 | { |
315 | 0 | int handle = zend_internal_function_extension_handles; |
316 | 0 | zend_internal_function_extension_handles += handles; |
317 | 0 | zend_add_system_entropy(module_name, "zend_get_internal_function_extension_handle", &zend_internal_function_extension_handles, sizeof(int)); |
318 | 0 | return handle; |
319 | 0 | } |
320 | | |
321 | 2.31k | ZEND_API size_t zend_internal_run_time_cache_reserved_size(void) { |
322 | 2.31k | return zend_internal_function_extension_handles * sizeof(void *); |
323 | 2.31k | } |
324 | | |
325 | 16 | ZEND_API void zend_init_internal_run_time_cache(void) { |
326 | 16 | size_t rt_size = zend_internal_run_time_cache_reserved_size(); |
327 | 16 | if (rt_size) { |
328 | 0 | size_t functions = zend_hash_num_elements(CG(function_table)); |
329 | 0 | zend_class_entry *ce; |
330 | 0 | ZEND_HASH_MAP_FOREACH_PTR(CG(class_table), ce) { |
331 | 0 | functions += zend_hash_num_elements(&ce->function_table); |
332 | 0 | } ZEND_HASH_FOREACH_END(); |
333 | | |
334 | 0 | size_t alloc_size = functions * rt_size; |
335 | 0 | char *ptr = pemalloc(alloc_size, 1); |
336 | |
|
337 | 0 | CG(internal_run_time_cache) = ptr; |
338 | 0 | CG(internal_run_time_cache_size) = alloc_size; |
339 | |
|
340 | 0 | zend_internal_function *zif; |
341 | 0 | ZEND_HASH_MAP_FOREACH_PTR(CG(function_table), zif) { |
342 | 0 | if (!ZEND_USER_CODE(zif->type) && ZEND_MAP_PTR_GET(zif->run_time_cache) == NULL) { |
343 | 0 | ZEND_MAP_PTR_SET(zif->run_time_cache, (void *)ptr); |
344 | 0 | ptr += rt_size; |
345 | 0 | } |
346 | 0 | } ZEND_HASH_FOREACH_END(); |
347 | 0 | ZEND_HASH_MAP_FOREACH_PTR(CG(class_table), ce) { |
348 | 0 | ZEND_HASH_MAP_FOREACH_PTR(&ce->function_table, zif) { |
349 | 0 | if (!ZEND_USER_CODE(zif->type) && ZEND_MAP_PTR_GET(zif->run_time_cache) == NULL) { |
350 | 0 | ZEND_MAP_PTR_SET(zif->run_time_cache, (void *)ptr); |
351 | 0 | ptr += rt_size; |
352 | 0 | } |
353 | 0 | } ZEND_HASH_FOREACH_END(); |
354 | 0 | } ZEND_HASH_FOREACH_END(); |
355 | 0 | } |
356 | 16 | } |
357 | | |
358 | 300k | ZEND_API void zend_reset_internal_run_time_cache(void) { |
359 | 300k | if (CG(internal_run_time_cache)) { |
360 | 0 | memset(CG(internal_run_time_cache), 0, CG(internal_run_time_cache_size)); |
361 | 0 | } |
362 | 300k | } |
363 | | |
364 | | ZEND_API zend_extension *zend_get_extension(const char *extension_name) |
365 | 4 | { |
366 | 4 | zend_llist_element *element; |
367 | | |
368 | 4 | for (element = zend_extensions.head; element; element = element->next) { |
369 | 0 | zend_extension *extension = (zend_extension *) element->data; |
370 | |
|
371 | 0 | if (!strcmp(extension->name, extension_name)) { |
372 | 0 | return extension; |
373 | 0 | } |
374 | 0 | } |
375 | 4 | return NULL; |
376 | 4 | } |
377 | | |
378 | | typedef struct _zend_extension_persist_data { |
379 | | zend_op_array *op_array; |
380 | | size_t size; |
381 | | char *mem; |
382 | | } zend_extension_persist_data; |
383 | | |
384 | | static void zend_extension_op_array_persist_calc_handler(zend_extension *extension, zend_extension_persist_data *data) |
385 | 0 | { |
386 | 0 | if (extension->op_array_persist_calc) { |
387 | 0 | data->size += extension->op_array_persist_calc(data->op_array); |
388 | 0 | } |
389 | 0 | } |
390 | | |
391 | | static void zend_extension_op_array_persist_handler(zend_extension *extension, zend_extension_persist_data *data) |
392 | 0 | { |
393 | 0 | if (extension->op_array_persist) { |
394 | 0 | size_t size = extension->op_array_persist(data->op_array, data->mem); |
395 | 0 | if (size) { |
396 | 0 | data->mem = (void*)((char*)data->mem + size); |
397 | 0 | data->size += size; |
398 | 0 | } |
399 | 0 | } |
400 | 0 | } |
401 | | |
402 | | ZEND_API size_t zend_extensions_op_array_persist_calc(zend_op_array *op_array) |
403 | 109k | { |
404 | 109k | if (zend_extension_flags & ZEND_EXTENSIONS_HAVE_OP_ARRAY_PERSIST_CALC) { |
405 | 0 | zend_extension_persist_data data; |
406 | |
|
407 | 0 | data.op_array = op_array; |
408 | 0 | data.size = 0; |
409 | 0 | data.mem = NULL; |
410 | 0 | zend_llist_apply_with_argument(&zend_extensions, (llist_apply_with_arg_func_t) zend_extension_op_array_persist_calc_handler, &data); |
411 | 0 | return data.size; |
412 | 0 | } |
413 | 109k | return 0; |
414 | 109k | } |
415 | | |
416 | | ZEND_API size_t zend_extensions_op_array_persist(zend_op_array *op_array, void *mem) |
417 | 109k | { |
418 | 109k | if (zend_extension_flags & ZEND_EXTENSIONS_HAVE_OP_ARRAY_PERSIST) { |
419 | 0 | zend_extension_persist_data data; |
420 | |
|
421 | 0 | data.op_array = op_array; |
422 | 0 | data.size = 0; |
423 | 0 | data.mem = mem; |
424 | 0 | zend_llist_apply_with_argument(&zend_extensions, (llist_apply_with_arg_func_t) zend_extension_op_array_persist_handler, &data); |
425 | 0 | return data.size; |
426 | 0 | } |
427 | 109k | return 0; |
428 | 109k | } |