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