Coverage Report

Created: 2025-06-13 06:43

/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
}