/src/php-src/ext/standard/dl.c
Line | Count | Source (jump to first uncovered line) |
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 | 0 | #define GET_DL_ERROR() DL_ERROR() |
38 | | #endif |
39 | | #endif /* defined(HAVE_LIBDL) */ |
40 | | |
41 | | /* {{{ Load a PHP extension at runtime */ |
42 | | PHPAPI PHP_FUNCTION(dl) |
43 | 0 | { |
44 | 0 | char *filename; |
45 | 0 | size_t filename_len; |
46 | |
|
47 | 0 | ZEND_PARSE_PARAMETERS_START(1, 1) |
48 | 0 | Z_PARAM_STRING(filename, filename_len) |
49 | 0 | ZEND_PARSE_PARAMETERS_END(); |
50 | | |
51 | 0 | if (!PG(enable_dl)) { |
52 | 0 | php_error_docref(NULL, E_WARNING, "Dynamically loaded extensions aren't enabled"); |
53 | 0 | RETURN_FALSE; |
54 | 0 | } |
55 | | |
56 | 0 | if (filename_len >= MAXPATHLEN) { |
57 | 0 | php_error_docref(NULL, E_WARNING, "Filename exceeds the maximum allowed length of %d characters", MAXPATHLEN); |
58 | 0 | RETURN_FALSE; |
59 | 0 | } |
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 | 0 | php_dl(filename, MODULE_TEMPORARY, return_value, 0); |
70 | 0 | if (Z_TYPE_P(return_value) == IS_TRUE) { |
71 | 0 | EG(full_tables_cleanup) = 1; |
72 | 0 | } |
73 | |
|
74 | | #if ZEND_RC_DEBUG |
75 | | zend_rc_debug = orig_rc_debug; |
76 | | #endif |
77 | 0 | } |
78 | | /* }}} */ |
79 | | |
80 | | #ifdef HAVE_LIBDL |
81 | | |
82 | | /* {{{ php_load_shlib */ |
83 | | PHPAPI void *php_load_shlib(const char *path, char **errp) |
84 | 0 | { |
85 | 0 | void *handle; |
86 | 0 | char *err; |
87 | |
|
88 | 0 | handle = DL_LOAD(path); |
89 | 0 | if (!handle) { |
90 | 0 | 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 | 0 | (*errp) = estrdup(err); |
102 | 0 | GET_DL_ERROR(); /* free the buffer storing the error */ |
103 | 0 | #endif |
104 | 0 | } |
105 | 0 | return handle; |
106 | 0 | } |
107 | | /* }}} */ |
108 | | |
109 | | /* {{{ php_load_extension */ |
110 | | PHPAPI int php_load_extension(const char *filename, int type, int start_now) |
111 | 0 | { |
112 | 0 | void *handle; |
113 | 0 | char *libpath; |
114 | 0 | zend_module_entry *module_entry; |
115 | 0 | zend_module_entry *(*get_module)(void); |
116 | 0 | int error_type, slash_suffix = 0; |
117 | 0 | char *extension_dir; |
118 | 0 | char *err1, *err2; |
119 | |
|
120 | 0 | if (type == MODULE_PERSISTENT) { |
121 | 0 | extension_dir = INI_STR("extension_dir"); |
122 | 0 | } else { |
123 | 0 | extension_dir = PG(extension_dir); |
124 | 0 | } |
125 | |
|
126 | 0 | if (type == MODULE_TEMPORARY) { |
127 | 0 | error_type = E_WARNING; |
128 | 0 | } else { |
129 | 0 | error_type = E_CORE_WARNING; |
130 | 0 | } |
131 | | |
132 | | /* Check if passed filename contains directory separators */ |
133 | 0 | if (strchr(filename, '/') != NULL || strchr(filename, DEFAULT_SLASH) != NULL) { |
134 | | /* Passing modules with full path is not supported for dynamically loaded extensions */ |
135 | 0 | if (type == MODULE_TEMPORARY) { |
136 | 0 | php_error_docref(NULL, E_WARNING, "Temporary module name should contain only filename"); |
137 | 0 | return FAILURE; |
138 | 0 | } |
139 | 0 | libpath = estrdup(filename); |
140 | 0 | } else if (extension_dir && extension_dir[0]) { |
141 | 0 | slash_suffix = IS_SLASH(extension_dir[strlen(extension_dir)-1]); |
142 | | /* Try as filename first */ |
143 | 0 | if (slash_suffix) { |
144 | 0 | spprintf(&libpath, 0, "%s%s", extension_dir, filename); /* SAFE */ |
145 | 0 | } else { |
146 | 0 | spprintf(&libpath, 0, "%s%c%s", extension_dir, DEFAULT_SLASH, filename); /* SAFE */ |
147 | 0 | } |
148 | 0 | } else { |
149 | 0 | return FAILURE; /* Not full path given or extension_dir is not set */ |
150 | 0 | } |
151 | | |
152 | 0 | handle = php_load_shlib(libpath, &err1); |
153 | 0 | if (!handle) { |
154 | | /* Now, consider 'filename' as extension name and build file name */ |
155 | 0 | char *orig_libpath = libpath; |
156 | |
|
157 | 0 | if (slash_suffix) { |
158 | 0 | spprintf(&libpath, 0, "%s" PHP_SHLIB_EXT_PREFIX "%s." PHP_SHLIB_SUFFIX, extension_dir, filename); /* SAFE */ |
159 | 0 | } else { |
160 | 0 | spprintf(&libpath, 0, "%s%c" PHP_SHLIB_EXT_PREFIX "%s." PHP_SHLIB_SUFFIX, extension_dir, DEFAULT_SLASH, filename); /* SAFE */ |
161 | 0 | } |
162 | |
|
163 | 0 | handle = php_load_shlib(libpath, &err2); |
164 | 0 | if (!handle) { |
165 | 0 | php_error_docref(NULL, error_type, "Unable to load dynamic library '%s' (tried: %s (%s), %s (%s))", |
166 | 0 | filename, orig_libpath, err1, libpath, err2); |
167 | 0 | efree(orig_libpath); |
168 | 0 | efree(err1); |
169 | 0 | efree(libpath); |
170 | 0 | efree(err2); |
171 | 0 | return FAILURE; |
172 | 0 | } |
173 | 0 | efree(orig_libpath); |
174 | 0 | efree(err1); |
175 | 0 | } |
176 | 0 | efree(libpath); |
177 | |
|
178 | | #ifdef PHP_WIN32 |
179 | | if (!php_win32_image_compatible(handle, &err1)) { |
180 | | php_error_docref(NULL, error_type, "%s", err1); |
181 | | efree(err1); |
182 | | DL_UNLOAD(handle); |
183 | | return FAILURE; |
184 | | } |
185 | | #endif |
186 | |
|
187 | 0 | 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 | 0 | if (!get_module) { |
194 | 0 | get_module = (zend_module_entry *(*)(void)) DL_FETCH_SYMBOL(handle, "_get_module"); |
195 | 0 | } |
196 | |
|
197 | 0 | if (!get_module) { |
198 | 0 | if (DL_FETCH_SYMBOL(handle, "zend_extension_entry") || DL_FETCH_SYMBOL(handle, "_zend_extension_entry")) { |
199 | 0 | DL_UNLOAD(handle); |
200 | 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); |
201 | 0 | return FAILURE; |
202 | 0 | } |
203 | 0 | DL_UNLOAD(handle); |
204 | 0 | php_error_docref(NULL, error_type, "Invalid library (maybe not a PHP library) '%s'", filename); |
205 | 0 | return FAILURE; |
206 | 0 | } |
207 | 0 | module_entry = get_module(); |
208 | 0 | if (zend_hash_str_exists(&module_registry, module_entry->name, strlen(module_entry->name))) { |
209 | 0 | zend_error(E_CORE_WARNING, "Module \"%s\" is already loaded", module_entry->name); |
210 | 0 | DL_UNLOAD(handle); |
211 | 0 | return FAILURE; |
212 | 0 | } |
213 | 0 | if (module_entry->zend_api != ZEND_MODULE_API_NO) { |
214 | 0 | php_error_docref(NULL, error_type, |
215 | 0 | "%s: Unable to initialize module\n" |
216 | 0 | "Module compiled with module API=%d\n" |
217 | 0 | "PHP compiled with module API=%d\n" |
218 | 0 | "These options need to match\n", |
219 | 0 | module_entry->name, module_entry->zend_api, ZEND_MODULE_API_NO); |
220 | 0 | DL_UNLOAD(handle); |
221 | 0 | return FAILURE; |
222 | 0 | } |
223 | 0 | if(strcmp(module_entry->build_id, ZEND_MODULE_BUILD_ID)) { |
224 | 0 | php_error_docref(NULL, error_type, |
225 | 0 | "%s: Unable to initialize module\n" |
226 | 0 | "Module compiled with build ID=%s\n" |
227 | 0 | "PHP compiled with build ID=%s\n" |
228 | 0 | "These options need to match\n", |
229 | 0 | module_entry->name, module_entry->build_id, ZEND_MODULE_BUILD_ID); |
230 | 0 | DL_UNLOAD(handle); |
231 | 0 | return FAILURE; |
232 | 0 | } |
233 | | |
234 | 0 | if ((module_entry = zend_register_module_ex(module_entry, type)) == NULL) { |
235 | 0 | DL_UNLOAD(handle); |
236 | 0 | return FAILURE; |
237 | 0 | } |
238 | | |
239 | 0 | module_entry->handle = handle; |
240 | |
|
241 | 0 | if ((type == MODULE_TEMPORARY || start_now) && zend_startup_module_ex(module_entry) == FAILURE) { |
242 | 0 | DL_UNLOAD(handle); |
243 | 0 | return FAILURE; |
244 | 0 | } |
245 | | |
246 | 0 | if ((type == MODULE_TEMPORARY || start_now) && module_entry->request_startup_func) { |
247 | 0 | if (module_entry->request_startup_func(type, module_entry->module_number) == FAILURE) { |
248 | 0 | php_error_docref(NULL, error_type, "Unable to initialize module '%s'", module_entry->name); |
249 | 0 | DL_UNLOAD(handle); |
250 | 0 | return FAILURE; |
251 | 0 | } |
252 | 0 | } |
253 | 0 | return SUCCESS; |
254 | 0 | } |
255 | | /* }}} */ |
256 | | |
257 | | #else |
258 | | |
259 | | static void php_dl_error(const char *filename) |
260 | | { |
261 | | php_error_docref(NULL, E_WARNING, "Cannot dynamically load %s - dynamic modules are not supported", filename); |
262 | | } |
263 | | |
264 | | PHPAPI void *php_load_shlib(const char *path, char **errp) |
265 | | { |
266 | | php_dl_error(path); |
267 | | (*errp) = estrdup("No DL support"); |
268 | | return NULL; |
269 | | } |
270 | | |
271 | | PHPAPI int php_load_extension(const char *filename, int type, int start_now) |
272 | | { |
273 | | php_dl_error(filename); |
274 | | |
275 | | return FAILURE; |
276 | | } |
277 | | |
278 | | #endif |
279 | | |
280 | | /* {{{ php_dl */ |
281 | | PHPAPI void php_dl(const char *file, int type, zval *return_value, int start_now) |
282 | 0 | { |
283 | | /* Load extension */ |
284 | 0 | if (php_load_extension(file, type, start_now) == FAILURE) { |
285 | 0 | RETVAL_FALSE; |
286 | 0 | } else { |
287 | 0 | RETVAL_TRUE; |
288 | 0 | } |
289 | 0 | } |
290 | | /* }}} */ |
291 | | |
292 | | PHP_MINFO_FUNCTION(dl) |
293 | 5 | { |
294 | 5 | #if defined(HAVE_LIBDL) |
295 | 5 | #define PHP_DL_SUPPORT_STATUS "enabled" |
296 | | #else |
297 | | #define PHP_DL_SUPPORT_STATUS "unavailable" |
298 | | #endif |
299 | 5 | php_info_print_table_row(2, "Dynamic Library Support", PHP_DL_SUPPORT_STATUS); |
300 | 5 | } |