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