Coverage Report

Created: 2026-06-02 06:39

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