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