PHP 8 compatibility
[m6w6/ext-psi] / src / module.c
1 /*******************************************************************************
2 Copyright (c) 2016, Michael Wallner <mike@php.net>.
3 All rights reserved.
4
5 Redistribution and use in source and binary forms, with or without
6 modification, are permitted provided that the following conditions are met:
7
8 * Redistributions of source code must retain the above copyright notice,
9 this list of conditions and the following disclaimer.
10 * Redistributions in binary form must reproduce the above copyright
11 notice, this list of conditions and the following disclaimer in the
12 documentation and/or other materials provided with the distribution.
13
14 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
15 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
17 DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
18 FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
20 SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
21 CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
22 OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 *******************************************************************************/
25
26 #ifdef HAVE_CONFIG_H
27 # include "config.h"
28 #else
29 # include "php_config.h"
30 #endif
31
32 #include "php.h"
33 #include "php_ini.h"
34 #include "ext/standard/info.h"
35 #include "zend_constants.h"
36 #include "zend_operators.h"
37
38 #include "php_psi.h"
39 #include "token.h"
40 #include "parser.h"
41 #include "cpp.h"
42
43 #if HAVE_LIBJIT
44 # include "libjit.h"
45 # ifndef HAVE_LIBFFI
46 # define PSI_ENGINE "jit"
47 # endif
48 #endif
49 #if HAVE_LIBFFI
50 # include "libffi.h"
51 # define PSI_ENGINE "ffi"
52 #endif
53
54 ZEND_DECLARE_MODULE_GLOBALS(psi);
55
56 //#define ZEND_INI_MH(name) int name(zend_ini_entry *entry, zend_string *new_value, void *mh_arg1, void *mh_arg2, void *mh_arg3, int stage)
57
58 static void OnUpdateBlacklist(const char *str, void (*cb)(const char*, size_t))
59 {
60 const char *end;
61
62 do {
63 size_t len;
64
65 end = strchr(str, ',');
66 if (end) {
67 len = end - str;
68 } else {
69 len = strlen(str);
70 }
71 if (len) {
72 cb(str, len);
73 }
74
75 str = end + 1;
76 } while (end);
77 }
78
79 static ZEND_INI_MH(OnUpdateBlacklistedDecls)
80 {
81 OnUpdateBlacklist(new_value->val, psi_blacklist_add_decl);
82 return SUCCESS;
83 }
84
85 static ZEND_INI_MH(OnUpdateBlacklistedVars)
86 {
87 OnUpdateBlacklist(new_value->val, psi_blacklist_add_var);
88 return SUCCESS;
89 }
90
91 static void OnDisplayBlacklist(struct psi_plist *bl)
92 {
93 size_t i = 0;
94 char *item;
95
96 while (psi_plist_get(bl, i++, &item)) {
97 if (i > 1) {
98 PUTS(",");
99 }
100 PUTS(item);
101 }
102 }
103 static ZEND_INI_DISP(OnDisplayBlacklistedDecls)
104 {
105 OnDisplayBlacklist(PSI_G(blacklist).decls);
106 }
107 static ZEND_INI_DISP(OnDisplayBlacklistedVars)
108 {
109 OnDisplayBlacklist(PSI_G(blacklist).vars);
110 }
111
112 PHP_INI_BEGIN()
113 STD_PHP_INI_ENTRY("psi.engine", PSI_ENGINE, PHP_INI_SYSTEM, OnUpdateString, engine, zend_psi_globals, psi_globals)
114 STD_PHP_INI_ENTRY("psi.directory", "psi.d", PHP_INI_SYSTEM, OnUpdateString, directory, zend_psi_globals, psi_globals)
115 PHP_INI_ENTRY_EX("psi.blacklist.decls", "", PHP_INI_SYSTEM, OnUpdateBlacklistedDecls, OnDisplayBlacklistedDecls)
116 PHP_INI_ENTRY_EX("psi.blacklist.vars", "", PHP_INI_SYSTEM, OnUpdateBlacklistedVars, OnDisplayBlacklistedVars)
117 PHP_INI_END();
118
119 static zend_object_handlers psi_object_handlers;
120 static zend_class_entry *psi_class_entry;
121
122 zend_class_entry *psi_object_get_class_entry();
123 zend_class_entry *psi_object_get_class_entry()
124 {
125 return psi_class_entry;
126 }
127
128 static void psi_object_free(zend_object *o)
129 {
130 psi_object *obj = PSI_OBJ(NULL, o);
131
132 if (obj->data) {
133 if (obj->dtor) {
134 obj->dtor(obj->data);
135 }
136 obj->data = NULL;
137 }
138 zend_object_std_dtor(o);
139 }
140
141 zend_object *psi_object_init_ex(zend_class_entry *ce, void *data, void (*dtor)(void *))
142 {
143 psi_object *o;
144
145 if (!ce) {
146 ce = psi_class_entry;
147 }
148
149 o = ecalloc(1, sizeof(*o) + zend_object_properties_size(ce));
150
151 o->data = data;
152 o->dtor = dtor;
153
154 zend_object_std_init(&o->std, ce);
155 object_properties_init(&o->std, ce);
156 o->std.handlers = &psi_object_handlers;
157 return &o->std;
158 }
159
160 zend_object *psi_object_init(zend_class_entry *ce)
161 {
162 return psi_object_init_ex(ce, NULL, NULL);
163 }
164
165 ZEND_BEGIN_ARG_INFO_EX(ai_psi_dump, 0, 0, 0)
166 ZEND_ARG_INFO(0, stream)
167 ZEND_END_ARG_INFO();
168 static PHP_FUNCTION(psi_dump)
169 {
170 php_stream *s;
171 zval *r = NULL;
172 struct psi_dump dump = {.fun = (psi_dump_cb) php_stream_printf};
173
174 if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS(), "|r!", &r)) {
175 return;
176 }
177 if (r) {
178 php_stream_from_zval(s, r);
179 dump.ctx.hn = s;
180 } else {
181 dump.ctx.hn = php_stream_open_wrapper("php://output", "w", REPORT_ERRORS, NULL);
182 }
183 psi_context_dump(&dump, PSI_G(context));
184 }
185
186 ZEND_BEGIN_ARG_INFO_EX(ai_psi_validate, 0, 0, 1)
187 ZEND_ARG_INFO(0, file)
188 ZEND_ARG_INFO(0, flags)
189 ZEND_ARG_INFO(1, errcnt)
190 ZEND_END_ARG_INFO();
191 static PHP_FUNCTION(psi_validate)
192 {
193 zend_string *file;
194 struct psi_parser_input *I;
195 struct psi_parser P;
196 struct psi_data D = {0};
197 struct psi_validate_scope S = {0};
198 zend_long flags = 0;
199 zval *errcnt = NULL;
200
201 #if PHP_DEBUG
202 if (psi_check_env("PSI_DEBUG")) {
203 flags |= PSI_DEBUG;
204 }
205 #endif
206
207 if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS(), "P|lz", &file, &flags, &errcnt)) {
208 return;
209 }
210
211 if (!psi_parser_init(&P, psi_error_wrapper, flags)) {
212 RETURN_FALSE;
213 }
214 if (!(I = psi_parser_open_file(&P, file->val, true))) {
215 psi_parser_dtor(&P);
216 RETURN_FALSE;
217 }
218
219 psi_parser_parse(&P, I);
220 psi_data_ctor(&D, P.error, P.flags);
221 psi_validate_scope_ctor(&S);
222 S.cpp = P.preproc;
223
224 RETVAL_BOOL(psi_validate(&S, &D, PSI_DATA(&P)));
225
226 if (errcnt) {
227 ZVAL_DEREF(errcnt);
228 convert_to_long(errcnt);
229 ZVAL_LONG(errcnt, P.errors);
230 }
231
232 psi_validate_scope_dtor(&S);
233 psi_data_dtor(&D);
234 psi_parser_dtor(&P);
235 psi_parser_input_free(&I);
236 }
237
238 ZEND_BEGIN_ARG_INFO_EX(ai_psi_validate_string, 0, 0, 1)
239 ZEND_ARG_INFO(0, string)
240 ZEND_ARG_INFO(0, flags)
241 ZEND_ARG_INFO(1, errcnt)
242 ZEND_END_ARG_INFO();
243 static PHP_FUNCTION(psi_validate_string)
244 {
245 zend_string *string;
246 struct psi_parser_input *I;
247 struct psi_parser P;
248 struct psi_data D = {0};
249 struct psi_validate_scope S = {0};
250 zend_long flags = 0;
251 zval *errcnt = NULL;
252
253 #if PHP_DEBUG
254 if (psi_check_env("PSI_DEBUG")) {
255 flags |= PSI_DEBUG;
256 }
257 #endif
258
259 if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS(), "S|lz", &string, &flags, &errcnt)) {
260 return;
261 }
262
263 if (!psi_parser_init(&P, psi_error_wrapper, flags)) {
264 RETURN_FALSE;
265 }
266 if (!(I = psi_parser_open_string(&P, string->val, string->len))) {
267 psi_parser_dtor(&P);
268 RETURN_FALSE;
269 }
270
271 psi_parser_parse(&P, I);
272 psi_data_ctor(&D, P.error, P.flags);
273 psi_validate_scope_ctor(&S);
274 S.cpp = P.preproc;
275
276 RETVAL_BOOL(psi_validate(&S, &D, PSI_DATA(&P)));
277
278 if (errcnt) {
279 ZVAL_DEREF(errcnt);
280 convert_to_long(errcnt);
281 ZVAL_LONG(errcnt, P.errors);
282 }
283
284 psi_validate_scope_dtor(&S);
285 psi_data_dtor(&D);
286 psi_parser_dtor(&P);
287 psi_parser_input_free(&I);
288 }
289
290 PHP_MINIT_FUNCTION(psi_debug);
291 PHP_MINIT_FUNCTION(psi_cpp);
292 PHP_MINIT_FUNCTION(psi_builtin);
293 PHP_MINIT_FUNCTION(psi_context);
294 static PHP_MINIT_FUNCTION(psi)
295 {
296 zend_class_entry ce = {0};
297
298 REGISTER_INI_ENTRIES();
299
300 zend_register_long_constant(ZEND_STRL("PSI_DEBUG"), PSI_DEBUG, CONST_CS|CONST_PERSISTENT, module_number);
301 zend_register_long_constant(ZEND_STRL("PSI_SILENT"), PSI_SILENT, CONST_CS|CONST_PERSISTENT, module_number);
302
303 INIT_NS_CLASS_ENTRY(ce, "psi", "object", NULL);
304 psi_class_entry = zend_register_internal_class_ex(&ce, NULL);
305 psi_class_entry->create_object = psi_object_init;
306
307 memcpy(&psi_object_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers));
308 psi_object_handlers.offset = XtOffsetOf(psi_object, std);
309 psi_object_handlers.free_obj = psi_object_free;
310 psi_object_handlers.clone_obj = NULL;
311
312 if (SUCCESS != PHP_MINIT(psi_debug)(type, module_number)) {
313 return FAILURE;
314 }
315 if (SUCCESS != PHP_MINIT(psi_builtin)(type, module_number)) {
316 return FAILURE;
317 }
318 if (SUCCESS != PHP_MINIT(psi_cpp)(type, module_number)) {
319 return FAILURE;
320 }
321 if (SUCCESS != PHP_MINIT(psi_context)(type, module_number)) {
322 return FAILURE;
323 }
324
325 return SUCCESS;
326 }
327
328 PHP_MSHUTDOWN_FUNCTION(psi_cpp);
329 PHP_MSHUTDOWN_FUNCTION(psi_builtin);
330 PHP_MSHUTDOWN_FUNCTION(psi_context);
331 PHP_MSHUTDOWN_FUNCTION(psi_debug);
332 static PHP_MSHUTDOWN_FUNCTION(psi)
333 {
334 PHP_MSHUTDOWN(psi_context)(type, module_number);
335 PHP_MSHUTDOWN(psi_cpp)(type, module_number);
336 PHP_MSHUTDOWN(psi_builtin)(type, module_number);
337 PHP_MSHUTDOWN(psi_debug)(type, module_number);
338
339 UNREGISTER_INI_ENTRIES();
340
341 return SUCCESS;
342 }
343
344 #if defined(COMPILE_DL_PSI) && defined(ZTS)
345 static PHP_RINIT_FUNCTION(psi)
346 {
347 ZEND_TSRMLS_CACHE_UPDATE();
348 return SUCCESS;
349 }
350 #endif
351
352 static PHP_MINFO_FUNCTION(psi)
353 {
354 php_info_print_table_start();
355 php_info_print_table_header(2, "PSI Support", "enabled");
356 php_info_print_table_row(2, "Extension Version", PHP_PSI_VERSION);
357 php_info_print_table_row(2, "Search Path", PSI_G(search_path));
358 php_info_print_table_end();
359
360 php_info_print_table_start();
361 php_info_print_table_header(3, "Used Library", "Compiled", "Linked");
362 php_info_print_table_row(3, "libffi",
363 #ifndef PHP_PSI_LIBFFI_VERSION
364 # define PHP_PSI_LIBFFI_VERSION "unknown"
365 #endif
366 #ifdef HAVE_LIBFFI
367 PHP_PSI_LIBFFI_VERSION, "unknown"
368 #else
369 "disabled", "disabled"
370 #endif
371 );
372 php_info_print_table_row(3, "libjit",
373 #ifdef HAVE_LIBJIT
374 "unknown", "unknown"
375 #else
376 "disabled", "disabled"
377 #endif
378 );
379
380 DISPLAY_INI_ENTRIES();
381 }
382
383 static void ptr_free(void *ptr)
384 {
385 free(*(void **) ptr);
386 }
387
388 void psi_blacklist_add_decl(const char *pattern, size_t len)
389 {
390 char *tmp = strndup(pattern, len);
391 struct psi_plist **decls = &PSI_G(blacklist).decls;
392
393 *decls = psi_plist_add(*decls, &tmp);
394 }
395
396 void psi_blacklist_add_var(const char *pattern, size_t len)
397 {
398 char *tmp = strndup(pattern, len);
399 struct psi_plist **vars = &PSI_G(blacklist).vars;
400
401 *vars = psi_plist_add(*vars, &tmp);
402 }
403
404 static PHP_GINIT_FUNCTION(psi)
405 {
406 char *tmp;
407 struct psi_plist **bl_decls = &psi_globals->blacklist.decls;
408 struct psi_plist **bl_vars = &psi_globals->blacklist.vars;
409
410 *bl_decls = psi_plist_init(ptr_free);
411 *bl_vars = psi_plist_init(ptr_free);
412
413 #define BL_ADD(D, d) \
414 tmp = strdup(d); \
415 *D = psi_plist_add(*D, &tmp)
416 #define BL_DECL_ADD(d) BL_ADD(bl_decls, d)
417 #define BL_VAR_ADD(d) BL_ADD(bl_vars, d)
418
419 BL_DECL_ADD("dlsym");
420 BL_DECL_ADD("alloca");
421 BL_DECL_ADD("atexit");
422 BL_DECL_ADD("at_quick_exit");
423
424 /* missing */
425 BL_DECL_ADD("_IO_cookie_init");
426 BL_DECL_ADD("bindresvport6");
427
428 /* va_list as arg */
429 BL_DECL_ADD("*v*printf");
430 BL_DECL_ADD("*v*scanf");
431 BL_DECL_ADD("vsyslog");
432
433 /* LFS/LFO for 32bit */
434 BL_DECL_ADD("*stat*64");
435 BL_DECL_ADD("*glob*64");
436 /* Hurd only */
437 BL_DECL_ADD("getumask");
438
439 /* using hidden structs */
440 BL_VAR_ADD("_IO_2_*");
441 }
442
443 static PHP_GSHUTDOWN_FUNCTION(psi)
444 {
445 psi_plist_free(psi_globals->blacklist.decls);
446 }
447
448 static const zend_function_entry psi_functions[] = {
449 PHP_FE(psi_dump, ai_psi_dump)
450 PHP_FE(psi_validate, ai_psi_validate)
451 PHP_FE(psi_validate_string, ai_psi_validate_string)
452 PHP_FE_END
453 };
454
455 static const zend_module_dep psi_deps[] = {
456 ZEND_MOD_REQUIRED("standard")
457 {0}
458 };
459
460 zend_module_entry psi_module_entry = {
461 STANDARD_MODULE_HEADER_EX,
462 NULL,
463 psi_deps,
464 "psi",
465 psi_functions,
466 PHP_MINIT(psi),
467 PHP_MSHUTDOWN(psi),
468 #if defined(COMPILE_DL_PSI) && defined(ZTS)
469 PHP_RINIT(psi), /* Replace with NULL if there's nothing to do at request start */
470 #else
471 NULL,
472 #endif
473 NULL,
474 PHP_MINFO(psi),
475 PHP_PSI_VERSION,
476 ZEND_MODULE_GLOBALS(psi),
477 PHP_GINIT(psi),
478 PHP_GSHUTDOWN(psi),
479 NULL, /* post-deactivate */
480 STANDARD_MODULE_PROPERTIES_EX
481 };
482
483 #ifdef COMPILE_DL_PSI
484 #ifdef ZTS
485 ZEND_TSRMLS_CACHE_DEFINE();
486 #endif
487 ZEND_GET_MODULE(psi)
488 #endif
489
490 /*
491 * Local variables:
492 * tab-width: 4
493 * c-basic-offset: 4
494 * End:
495 * vim600: noet sw=4 ts=4 fdm=marker
496 * vim<600: noet sw=4 ts=4
497 */