Coverage Report

Created: 2026-06-02 06:36

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/php-src/ext/standard/dl.c
Line
Count
Source
1
/*
2
   +----------------------------------------------------------------------+
3
   | Copyright © The PHP Group and Contributors.                          |
4
   +----------------------------------------------------------------------+
5
   | This source file is subject to the Modified BSD License that is      |
6
   | bundled with this package in the file LICENSE, and is available      |
7
   | through the World Wide Web at <https://www.php.net/license/>.        |
8
   |                                                                      |
9
   | SPDX-License-Identifier: BSD-3-Clause                                |
10
   +----------------------------------------------------------------------+
11
   | Authors: Brian Schaffner <brian@tool.net>                            |
12
   |          Shane Caraveo <shane@caraveo.com>                           |
13
   |          Zeev Suraski <zeev@php.net>                                 |
14
   +----------------------------------------------------------------------+
15
*/
16
17
#include "php.h"
18
#include "dl.h"
19
#include "php_globals.h"
20
#include "php_ini.h"
21
#include "ext/standard/info.h"
22
23
#include "SAPI.h"
24
25
#ifdef HAVE_LIBDL
26
#include <stdlib.h>
27
#include <stdio.h>
28
#include <string.h>
29
#ifdef PHP_WIN32
30
#include "win32/param.h"
31
#include "win32/winutil.h"
32
#define GET_DL_ERROR()  php_win_err()
33
#else
34
#include <sys/param.h>
35
0
#define GET_DL_ERROR()  DL_ERROR()
36
#endif
37
#endif /* defined(HAVE_LIBDL) */
38
39
/* {{{ Load a PHP extension at runtime */
40
PHPAPI PHP_FUNCTION(dl)
41
0
{
42
0
  char *filename;
43
0
  size_t filename_len;
44
45
0
  ZEND_PARSE_PARAMETERS_START(1, 1)
46
0
    Z_PARAM_STRING(filename, filename_len)
47
0
  ZEND_PARSE_PARAMETERS_END();
48
49
0
  if (!PG(enable_dl)) {
50
0
    php_error_docref(NULL, E_WARNING, "Dynamically loaded extensions aren't enabled");
51
0
    RETURN_FALSE;
52
0
  }
53
54
0
  if (filename_len >= MAXPATHLEN) {
55
0
    php_error_docref(NULL, E_WARNING, "Filename exceeds the maximum allowed length of %d characters", MAXPATHLEN);
56
0
    RETURN_FALSE;
57
0
  }
58
59
#if ZEND_RC_DEBUG
60
  bool orig_rc_debug = zend_rc_debug;
61
  /* FIXME: Loading extensions during the request breaks some invariants. In
62
   * particular, it will create persistent interned strings, which is not
63
   * allowed at this stage. */
64
  zend_rc_debug = false;
65
#endif
66
67
0
  php_dl(filename, MODULE_TEMPORARY, return_value, 0);
68
0
  if (Z_TYPE_P(return_value) == IS_TRUE) {
69
0
    EG(full_tables_cleanup) = 1;
70
0
  }
71
72
#if ZEND_RC_DEBUG
73
  zend_rc_debug = orig_rc_debug;
74
#endif
75
0
}
76
/* }}} */
77
78
#ifdef HAVE_LIBDL
79
80
/* {{{ php_load_shlib */
81
PHPAPI void *php_load_shlib(const char *path, char **errp)
82
0
{
83
0
  void *handle;
84
0
  char *err;
85
86
0
  handle = DL_LOAD(path);
87
0
  if (!handle) {
88
0
    err = GET_DL_ERROR();
89
#ifdef PHP_WIN32
90
    if (err && (*err)) {
91
      size_t i = strlen(err);
92
      (*errp)=estrdup(err);
93
      php_win32_error_msg_free(err);
94
      while (i > 0 && isspace((unsigned char)(*errp)[i-1])) { (*errp)[i-1] = '\0'; i--; }
95
    } else {
96
      (*errp) = estrdup("<No message>");
97
    }
98
#else
99
0
    (*errp) = estrdup(err);
100
0
    GET_DL_ERROR(); /* free the buffer storing the error */
101
0
#endif
102
0
  }
103
0
  return handle;
104
0
}
105
/* }}} */
106
107
/* {{{ php_load_extension */
108
PHPAPI int php_load_extension(const char *filename, int type, int start_now)
109
0
{
110
0
  void *handle;
111
0
  char *libpath;
112
0
  zend_module_entry *module_entry;
113
0
  zend_module_entry *(*get_module)(void);
114
0
  int error_type, slash_suffix = 0;
115
0
  const char *extension_dir;
116
0
  char *err1, *err2;
117
118
0
  if (type == MODULE_PERSISTENT) {
119
0
    extension_dir = zend_ini_string_literal("extension_dir");
120
0
  } else {
121
0
    extension_dir = PG(extension_dir);
122
0
  }
123
124
0
  if (type == MODULE_TEMPORARY) {
125
0
    error_type = E_WARNING;
126
0
  } else {
127
0
    error_type = E_CORE_WARNING;
128
0
  }
129
130
  /* Check if passed filename contains directory separators */
131
0
  if (strchr(filename, '/') != NULL || strchr(filename, DEFAULT_SLASH) != NULL) {
132
    /* Passing modules with full path is not supported for dynamically loaded extensions */
133
0
    if (type == MODULE_TEMPORARY) {
134
0
      php_error_docref(NULL, E_WARNING, "Temporary module name should contain only filename");
135
0
      return FAILURE;
136
0
    }
137
0
    libpath = estrdup(filename);
138
0
  } else if (extension_dir && extension_dir[0]) {
139
0
    slash_suffix = IS_SLASH(extension_dir[strlen(extension_dir)-1]);
140
    /* Try as filename first */
141
0
    if (slash_suffix) {
142
0
      spprintf(&libpath, 0, "%s%s", extension_dir, filename); /* SAFE */
143
0
    } else {
144
0
      spprintf(&libpath, 0, "%s%c%s", extension_dir, DEFAULT_SLASH, filename); /* SAFE */
145
0
    }
146
0
  } else {
147
0
    return FAILURE; /* Not full path given or extension_dir is not set */
148
0
  }
149
150
0
  handle = php_load_shlib(libpath, &err1);
151
0
  if (!handle) {
152
    /* Now, consider 'filename' as extension name and build file name */
153
0
    char *orig_libpath = libpath;
154
155
0
    if (slash_suffix) {
156
0
      spprintf(&libpath, 0, "%s" PHP_SHLIB_EXT_PREFIX "%s." PHP_SHLIB_SUFFIX, extension_dir, filename); /* SAFE */
157
0
    } else {
158
0
      spprintf(&libpath, 0, "%s%c" PHP_SHLIB_EXT_PREFIX "%s." PHP_SHLIB_SUFFIX, extension_dir, DEFAULT_SLASH, filename); /* SAFE */
159
0
    }
160
161
0
    handle = php_load_shlib(libpath, &err2);
162
0
    if (!handle) {
163
0
      php_error_docref(NULL, error_type, "Unable to load dynamic library '%s' (tried: %s (%s), %s (%s))",
164
0
        filename, orig_libpath, err1, libpath, err2);
165
0
      efree(orig_libpath);
166
0
      efree(err1);
167
0
      efree(libpath);
168
0
      efree(err2);
169
0
      return FAILURE;
170
0
    }
171
0
    efree(orig_libpath);
172
0
    efree(err1);
173
0
  }
174
0
  efree(libpath);
175
176
#ifdef PHP_WIN32
177
  if (!php_win32_image_compatible(handle, &err1)) {
178
      php_error_docref(NULL, error_type, "%s", err1);
179
      efree(err1);
180
      DL_UNLOAD(handle);
181
      return FAILURE;
182
  }
183
#endif
184
185
0
  get_module = (zend_module_entry *(*)(void)) DL_FETCH_SYMBOL(handle, "get_module");
186
187
  /* Some OS prepend _ to symbol names while their dynamic linker
188
   * does not do that automatically. Thus we check manually for
189
   * _get_module. */
190
191
0
  if (!get_module) {
192
0
    get_module = (zend_module_entry *(*)(void)) DL_FETCH_SYMBOL(handle, "_get_module");
193
0
  }
194
195
0
  if (!get_module) {
196
0
    if (DL_FETCH_SYMBOL(handle, "zend_extension_entry") || DL_FETCH_SYMBOL(handle, "_zend_extension_entry")) {
197
0
      DL_UNLOAD(handle);
198
0
      php_error_docref(NULL, error_type, "Invalid library (appears to be a Zend Extension, try loading using zend_extension=%s from php.ini)", filename);
199
0
      return FAILURE;
200
0
    }
201
0
    DL_UNLOAD(handle);
202
0
    php_error_docref(NULL, error_type, "Invalid library (maybe not a PHP library) '%s'", filename);
203
0
    return FAILURE;
204
0
  }
205
0
  module_entry = get_module();
206
0
  if (zend_hash_str_exists(&module_registry, module_entry->name, strlen(module_entry->name))) {
207
0
    zend_error(E_CORE_WARNING, "Module \"%s\" is already loaded", module_entry->name);
208
0
    DL_UNLOAD(handle);
209
0
    return FAILURE;
210
0
  }
211
0
  if (module_entry->zend_api != ZEND_MODULE_API_NO) {
212
0
      php_error_docref(NULL, error_type,
213
0
          "%s: Unable to initialize module\n"
214
0
          "Module compiled with module API=%d\n"
215
0
          "PHP    compiled with module API=%d\n"
216
0
          "These options need to match\n",
217
0
          module_entry->name, module_entry->zend_api, ZEND_MODULE_API_NO);
218
0
      DL_UNLOAD(handle);
219
0
      return FAILURE;
220
0
  }
221
0
  if(strcmp(module_entry->build_id, ZEND_MODULE_BUILD_ID)) {
222
0
    php_error_docref(NULL, error_type,
223
0
        "%s: Unable to initialize module\n"
224
0
        "Module compiled with build ID=%s\n"
225
0
        "PHP    compiled with build ID=%s\n"
226
0
        "These options need to match\n",
227
0
        module_entry->name, module_entry->build_id, ZEND_MODULE_BUILD_ID);
228
0
    DL_UNLOAD(handle);
229
0
    return FAILURE;
230
0
  }
231
232
0
  if ((module_entry = zend_register_module_ex(module_entry, type)) == NULL) {
233
0
    DL_UNLOAD(handle);
234
0
    return FAILURE;
235
0
  }
236
237
0
  module_entry->handle = handle;
238
239
0
  if ((type == MODULE_TEMPORARY || start_now) && zend_startup_module_ex(module_entry) == FAILURE) {
240
0
    DL_UNLOAD(handle);
241
0
    return FAILURE;
242
0
  }
243
244
0
  if ((type == MODULE_TEMPORARY || start_now) && module_entry->request_startup_func) {
245
0
    if (module_entry->request_startup_func(type, module_entry->module_number) == FAILURE) {
246
0
      php_error_docref(NULL, error_type, "Unable to initialize module '%s'", module_entry->name);
247
0
      DL_UNLOAD(handle);
248
0
      return FAILURE;
249
0
    }
250
0
  }
251
0
  return SUCCESS;
252
0
}
253
/* }}} */
254
255
#else
256
257
static void php_dl_error(const char *filename)
258
{
259
  php_error_docref(NULL, E_WARNING, "Cannot dynamically load %s - dynamic modules are not supported", filename);
260
}
261
262
PHPAPI void *php_load_shlib(const char *path, char **errp)
263
{
264
  php_dl_error(path);
265
  (*errp) = estrdup("No DL support");
266
  return NULL;
267
}
268
269
PHPAPI int php_load_extension(const char *filename, int type, int start_now)
270
{
271
  php_dl_error(filename);
272
273
  return FAILURE;
274
}
275
276
#endif
277
278
/* {{{ php_dl */
279
PHPAPI void php_dl(const char *file, int type, zval *return_value, int start_now)
280
0
{
281
  /* Load extension */
282
0
  if (php_load_extension(file, type, start_now) == FAILURE) {
283
0
    RETVAL_FALSE;
284
0
  } else {
285
0
    RETVAL_TRUE;
286
0
  }
287
0
}
288
/* }}} */
289
290
PHP_MINFO_FUNCTION(dl)
291
1
{
292
1
#if defined(HAVE_LIBDL)
293
1
#define PHP_DL_SUPPORT_STATUS "enabled"
294
#else
295
#define PHP_DL_SUPPORT_STATUS "unavailable"
296
#endif
297
1
  php_info_print_table_row(2, "Dynamic Library Support", PHP_DL_SUPPORT_STATUS);
298
1
}