de8b50ee500b40eca180309bc284e9b7e7fad6f4
[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 void psi_blacklist_add_decl(const char *pattern, size_t len);
80 static ZEND_INI_MH(OnUpdateBlacklistedDecls)
81 {
82 OnUpdateBlacklist(new_value->val, psi_blacklist_add_decl);
83 return SUCCESS;
84 }
85
86 static void psi_blacklist_add_var(const char *pattern, size_t len);
87 static ZEND_INI_MH(OnUpdateBlacklistedVars)
88 {
89 OnUpdateBlacklist(new_value->val, psi_blacklist_add_var);
90 return SUCCESS;
91 }
92
93 static void OnDisplayBlacklist(struct psi_plist *bl)
94 {
95 size_t i = 0;
96 char *item;
97
98 while (psi_plist_get(bl, i++, &item)) {
99 if (i > 1) {
100 PUTS(",");
101 }
102 PUTS(item);
103 }
104 }
105 static ZEND_INI_DISP(OnDisplayBlacklistedDecls)
106 {
107 OnDisplayBlacklist(PSI_G(blacklist).decls);
108 }
109 static ZEND_INI_DISP(OnDisplayBlacklistedVars)
110 {
111 OnDisplayBlacklist(PSI_G(blacklist).vars);
112 }
113
114 PHP_INI_BEGIN()
115 STD_PHP_INI_ENTRY("psi.engine", PSI_ENGINE, PHP_INI_SYSTEM, OnUpdateString, engine, zend_psi_globals, psi_globals)
116 STD_PHP_INI_ENTRY("psi.directory", "psi.d", PHP_INI_SYSTEM, OnUpdateString, directory, zend_psi_globals, psi_globals)
117 PHP_INI_ENTRY_EX("psi.blacklist.decls", "", PHP_INI_SYSTEM, OnUpdateBlacklistedDecls, OnDisplayBlacklistedDecls)
118 PHP_INI_ENTRY_EX("psi.blacklist.vars", "", PHP_INI_SYSTEM, OnUpdateBlacklistedVars, OnDisplayBlacklistedVars)
119 PHP_INI_END();
120
121 static zend_object_handlers psi_object_handlers;
122 static zend_class_entry *psi_class_entry;
123
124 zend_class_entry *psi_object_get_class_entry();
125 zend_class_entry *psi_object_get_class_entry()
126 {
127 return psi_class_entry;
128 }
129
130 static void psi_object_free(zend_object *o)
131 {
132 psi_object *obj = PSI_OBJ(NULL, o);
133
134 if (obj->data) {
135 if (obj->dtor) {
136 obj->dtor(obj->data);
137 }
138 obj->data = NULL;
139 }
140 zend_object_std_dtor(o);
141 }
142
143 zend_object *psi_object_init_ex(zend_class_entry *ce, void *data, void (*dtor)(void *))
144 {
145 psi_object *o;
146
147 if (!ce) {
148 ce = psi_class_entry;
149 }
150
151 o = ecalloc(1, sizeof(*o) + zend_object_properties_size(ce));
152
153 o->data = data;
154 o->dtor = dtor;
155
156 zend_object_std_init(&o->std, ce);
157 object_properties_init(&o->std, ce);
158 o->std.handlers = &psi_object_handlers;
159 return &o->std;
160 }
161
162 zend_object *psi_object_init(zend_class_entry *ce)
163 {
164 return psi_object_init_ex(ce, NULL, NULL);
165 }
166
167 ZEND_BEGIN_ARG_INFO_EX(ai_psi_dump, 0, 0, 0)
168 ZEND_ARG_INFO(0, stream)
169 ZEND_END_ARG_INFO();
170 static PHP_FUNCTION(psi_dump)
171 {
172 php_stream *s;
173 zval *r = NULL;
174 struct psi_dump dump = {.fun = (psi_dump_cb) php_stream_printf};
175
176 if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS(), "|r!", &r)) {
177 return;
178 }
179 if (r) {
180 php_stream_from_zval(s, r);
181 dump.ctx.hn = s;
182 } else {
183 dump.ctx.hn = php_stream_open_wrapper("php://output", "w", REPORT_ERRORS, NULL);
184 }
185 psi_context_dump(&dump, PSI_G(context));
186 }
187
188 ZEND_BEGIN_ARG_INFO_EX(ai_psi_validate, 0, 0, 1)
189 ZEND_ARG_INFO(0, file)
190 ZEND_ARG_INFO(0, flags)
191 ZEND_ARG_INFO(1, errcnt)
192 ZEND_END_ARG_INFO();
193 static PHP_FUNCTION(psi_validate)
194 {
195 zend_string *file;
196 struct psi_parser_input *I;
197 struct psi_parser P;
198 struct psi_data D = {0};
199 struct psi_validate_scope S = {0};
200 zend_long flags = 0;
201 zval *errcnt = NULL;
202
203 #if PHP_DEBUG
204 if (psi_check_env("PSI_DEBUG")) {
205 flags |= PSI_DEBUG;
206 }
207 #endif
208
209 if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS(), "P|lz", &file, &flags, &errcnt)) {
210 return;
211 }
212
213 if (!psi_parser_init(&P, psi_error_wrapper, flags)) {
214 RETURN_FALSE;
215 }
216 if (!(I = psi_parser_open_file(&P, file->val, true))) {
217 psi_parser_dtor(&P);
218 RETURN_FALSE;
219 }
220
221 psi_parser_parse(&P, I);
222 psi_data_ctor(&D, P.error, P.flags);
223 psi_validate_scope_ctor(&S);
224 S.cpp = P.preproc;
225
226 RETVAL_BOOL(psi_validate(&S, &D, PSI_DATA(&P)));
227
228 if (errcnt) {
229 ZVAL_DEREF(errcnt);
230 convert_to_long(errcnt);
231 ZVAL_LONG(errcnt, P.errors);
232 }
233
234 psi_validate_scope_dtor(&S);
235 psi_data_dtor(&D);
236 psi_parser_dtor(&P);
237 psi_parser_input_free(&I);
238 }
239
240 ZEND_BEGIN_ARG_INFO_EX(ai_psi_validate_string, 0, 0, 1)
241 ZEND_ARG_INFO(0, string)
242 ZEND_ARG_INFO(0, flags)
243 ZEND_ARG_INFO(1, errcnt)
244 ZEND_END_ARG_INFO();
245 static PHP_FUNCTION(psi_validate_string)
246 {
247 zend_string *string;
248 struct psi_parser_input *I;
249 struct psi_parser P;
250 struct psi_data D = {0};
251 struct psi_validate_scope S = {0};
252 zend_long flags = 0;
253 zval *errcnt = NULL;
254
255 #if PHP_DEBUG
256 if (psi_check_env("PSI_DEBUG")) {
257 flags |= PSI_DEBUG;
258 }
259 #endif
260
261 if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS(), "S|lz", &string, &flags, &errcnt)) {
262 return;
263 }
264
265 if (!psi_parser_init(&P, psi_error_wrapper, flags)) {
266 RETURN_FALSE;
267 }
268 if (!(I = psi_parser_open_string(&P, string->val, string->len))) {
269 psi_parser_dtor(&P);
270 RETURN_FALSE;
271 }
272
273 psi_parser_parse(&P, I);
274 psi_data_ctor(&D, P.error, P.flags);
275 psi_validate_scope_ctor(&S);
276 S.cpp = P.preproc;
277
278 RETVAL_BOOL(psi_validate(&S, &D, PSI_DATA(&P)));
279
280 if (errcnt) {
281 ZVAL_DEREF(errcnt);
282 convert_to_long(errcnt);
283 ZVAL_LONG(errcnt, P.errors);
284 }
285
286 psi_validate_scope_dtor(&S);
287 psi_data_dtor(&D);
288 psi_parser_dtor(&P);
289 psi_parser_input_free(&I);
290 }
291
292 PHP_MINIT_FUNCTION(psi_debug);
293 PHP_MINIT_FUNCTION(psi_cpp);
294 PHP_MINIT_FUNCTION(psi_builtin);
295 PHP_MINIT_FUNCTION(psi_context);
296 static PHP_MINIT_FUNCTION(psi)
297 {
298 zend_class_entry ce = {0};
299
300 REGISTER_INI_ENTRIES();
301
302 zend_register_long_constant(ZEND_STRL("PSI_DEBUG"), PSI_DEBUG, CONST_CS|CONST_PERSISTENT, module_number);
303 zend_register_long_constant(ZEND_STRL("PSI_SILENT"), PSI_SILENT, CONST_CS|CONST_PERSISTENT, module_number);
304
305 INIT_NS_CLASS_ENTRY(ce, "psi", "object", NULL);
306 psi_class_entry = zend_register_internal_class_ex(&ce, NULL);
307 psi_class_entry->create_object = psi_object_init;
308
309 memcpy(&psi_object_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers));
310 psi_object_handlers.offset = XtOffsetOf(psi_object, std);
311 psi_object_handlers.free_obj = psi_object_free;
312 psi_object_handlers.clone_obj = NULL;
313
314 if (SUCCESS != PHP_MINIT(psi_debug)(type, module_number)) {
315 return FAILURE;
316 }
317 if (SUCCESS != PHP_MINIT(psi_builtin)(type, module_number)) {
318 return FAILURE;
319 }
320 if (SUCCESS != PHP_MINIT(psi_cpp)(type, module_number)) {
321 return FAILURE;
322 }
323 if (SUCCESS != PHP_MINIT(psi_context)(type, module_number)) {
324 return FAILURE;
325 }
326
327 return SUCCESS;
328 }
329
330 PHP_MSHUTDOWN_FUNCTION(psi_cpp);
331 PHP_MSHUTDOWN_FUNCTION(psi_builtin);
332 PHP_MSHUTDOWN_FUNCTION(psi_context);
333 PHP_MSHUTDOWN_FUNCTION(psi_debug);
334 static PHP_MSHUTDOWN_FUNCTION(psi)
335 {
336 PHP_MSHUTDOWN(psi_context)(type, module_number);
337 PHP_MSHUTDOWN(psi_cpp)(type, module_number);
338 PHP_MSHUTDOWN(psi_builtin)(type, module_number);
339 PHP_MSHUTDOWN(psi_debug)(type, module_number);
340
341 UNREGISTER_INI_ENTRIES();
342
343 return SUCCESS;
344 }
345
346 #if defined(COMPILE_DL_PSI) && defined(ZTS)
347 static PHP_RINIT_FUNCTION(psi)
348 {
349 ZEND_TSRMLS_CACHE_UPDATE();
350 return SUCCESS;
351 }
352 #endif
353
354 static PHP_MINFO_FUNCTION(psi)
355 {
356 php_info_print_table_start();
357 php_info_print_table_header(2, "PSI Support", "enabled");
358 php_info_print_table_row(2, "Extension Version", PHP_PSI_VERSION);
359 php_info_print_table_row(2, "Search Path", PSI_G(search_path));
360 php_info_print_table_end();
361
362 php_info_print_table_start();
363 php_info_print_table_header(3, "Used Library", "Compiled", "Linked");
364 php_info_print_table_row(3, "libffi",
365 #ifndef PHP_PSI_LIBFFI_VERSION
366 # define PHP_PSI_LIBFFI_VERSION "unknown"
367 #endif
368 #ifdef HAVE_LIBFFI
369 PHP_PSI_LIBFFI_VERSION, "unknown"
370 #else
371 "disabled", "disabled"
372 #endif
373 );
374 php_info_print_table_row(3, "libjit",
375 #ifdef HAVE_LIBJIT
376 "unknown", "unknown"
377 #else
378 "disabled", "disabled"
379 #endif
380 );
381
382 DISPLAY_INI_ENTRIES();
383 }
384
385 static void ptr_free(void *ptr)
386 {
387 free(*(void **) ptr);
388 }
389
390 static void psi_blacklist_add_decl(const char *pattern, size_t len)
391 {
392 char *tmp = strndup(pattern, len);
393 struct psi_plist **decls = &PSI_G(blacklist).decls;
394
395 *decls = psi_plist_add(*decls, &tmp);
396 }
397
398 static void psi_blacklist_add_var(const char *pattern, size_t len)
399 {
400 char *tmp = strndup(pattern, len);
401 struct psi_plist **vars = &PSI_G(blacklist).vars;
402
403 *vars = psi_plist_add(*vars, &tmp);
404 }
405
406 static PHP_GINIT_FUNCTION(psi)
407 {
408 char *tmp;
409 struct psi_plist **bl_decls = &psi_globals->blacklist.decls;
410 struct psi_plist **bl_vars = &psi_globals->blacklist.vars;
411
412 *bl_decls = psi_plist_init(ptr_free);
413 *bl_vars = psi_plist_init(ptr_free);
414
415 #define BL_ADD(D, d) \
416 tmp = strdup(d); \
417 *D = psi_plist_add(*D, &tmp)
418 #define BL_DECL_ADD(d) BL_ADD(bl_decls, d)
419 #define BL_VAR_ADD(d) BL_ADD(bl_vars, d)
420
421 BL_DECL_ADD("dlsym");
422 BL_DECL_ADD("alloca");
423 BL_DECL_ADD("atexit");
424 BL_DECL_ADD("at_quick_exit");
425
426 /* missing */
427 BL_DECL_ADD("_IO_cookie_init");
428 BL_DECL_ADD("bindresvport6");
429
430 /* va_list as arg */
431 BL_DECL_ADD("*v*printf");
432 BL_DECL_ADD("*v*scanf");
433 BL_DECL_ADD("vsyslog");
434
435 /* LFS/LFO for 32bit */
436 BL_DECL_ADD("*stat*64");
437 BL_DECL_ADD("*glob*64");
438 /* Hurd only */
439 BL_DECL_ADD("getumask");
440
441 /* using hidden structs */
442 BL_VAR_ADD("_IO_2_*");
443 }
444
445 static PHP_GSHUTDOWN_FUNCTION(psi)
446 {
447 psi_plist_free(psi_globals->blacklist.decls);
448 }
449
450 static const zend_function_entry psi_functions[] = {
451 PHP_FE(psi_dump, ai_psi_dump)
452 PHP_FE(psi_validate, ai_psi_validate)
453 PHP_FE(psi_validate_string, ai_psi_validate_string)
454 PHP_FE_END
455 };
456
457 static const zend_module_dep psi_deps[] = {
458 ZEND_MOD_REQUIRED("standard")
459 {0}
460 };
461
462 zend_module_entry psi_module_entry = {
463 STANDARD_MODULE_HEADER_EX,
464 NULL,
465 psi_deps,
466 "psi",
467 psi_functions,
468 PHP_MINIT(psi),
469 PHP_MSHUTDOWN(psi),
470 #if defined(COMPILE_DL_PSI) && defined(ZTS)
471 PHP_RINIT(psi), /* Replace with NULL if there's nothing to do at request start */
472 #else
473 NULL,
474 #endif
475 NULL,
476 PHP_MINFO(psi),
477 PHP_PSI_VERSION,
478 ZEND_MODULE_GLOBALS(psi),
479 PHP_GINIT(psi),
480 PHP_GSHUTDOWN(psi),
481 NULL, /* post-deactivate */
482 STANDARD_MODULE_PROPERTIES_EX
483 };
484
485 #ifdef COMPILE_DL_PSI
486 #ifdef ZTS
487 ZEND_TSRMLS_CACHE_DEFINE();
488 #endif
489 ZEND_GET_MODULE(psi)
490 #endif
491
492 /*
493 * Local variables:
494 * tab-width: 4
495 * c-basic-offset: 4
496 * End:
497 * vim600: noet sw=4 ts=4 fdm=marker
498 * vim<600: noet sw=4 ts=4
499 */