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