0edd74f365ca0c6c28c879cac13c1b10010d837e
[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 int fd = STDOUT_FILENO;
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
177 if (SUCCESS != php_stream_cast(s, PHP_STREAM_AS_FD | PHP_STREAM_CAST_INTERNAL, (void **)&fd, 1)) {
178 RETURN_FALSE;
179 }
180 }
181 psi_context_dump(PSI_G(context), fd);
182 }
183
184 ZEND_BEGIN_ARG_INFO_EX(ai_psi_validate, 0, 0, 1)
185 ZEND_ARG_INFO(0, file)
186 ZEND_ARG_INFO(0, flags)
187 ZEND_ARG_INFO(1, errcnt)
188 ZEND_END_ARG_INFO();
189 static PHP_FUNCTION(psi_validate)
190 {
191 zend_string *file;
192 struct psi_parser_input *I;
193 struct psi_parser P;
194 struct psi_data D = {0};
195 struct psi_validate_scope S = {0};
196 zend_long flags = 0;
197 zval *errcnt = NULL;
198
199 #if PHP_DEBUG
200 if (psi_check_env("PSI_DEBUG")) {
201 flags |= PSI_DEBUG;
202 }
203 #endif
204
205 if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS(), "P|lz", &file, &flags, &errcnt)) {
206 return;
207 }
208
209 if (!psi_parser_init(&P, psi_error_wrapper, flags)) {
210 RETURN_FALSE;
211 }
212 if (!(I = psi_parser_open_file(&P, file->val, true))) {
213 psi_parser_dtor(&P);
214 RETURN_FALSE;
215 }
216
217 psi_parser_parse(&P, I);
218 psi_data_ctor(&D, P.error, P.flags);
219 psi_validate_scope_ctor(&S);
220 S.cpp = P.preproc;
221
222 RETVAL_BOOL(psi_validate(&S, &D, PSI_DATA(&P)));
223
224 if (errcnt) {
225 ZVAL_DEREF(errcnt);
226 convert_to_long(errcnt);
227 ZVAL_LONG(errcnt, P.errors);
228 }
229
230 psi_validate_scope_dtor(&S);
231 psi_data_dtor(&D);
232 psi_parser_dtor(&P);
233 psi_parser_input_free(&I);
234 }
235
236 ZEND_BEGIN_ARG_INFO_EX(ai_psi_validate_string, 0, 0, 1)
237 ZEND_ARG_INFO(0, string)
238 ZEND_ARG_INFO(0, flags)
239 ZEND_ARG_INFO(1, errcnt)
240 ZEND_END_ARG_INFO();
241 static PHP_FUNCTION(psi_validate_string)
242 {
243 zend_string *string;
244 struct psi_parser_input *I;
245 struct psi_parser P;
246 struct psi_data D = {0};
247 struct psi_validate_scope S = {0};
248 zend_long flags = 0;
249 zval *errcnt = NULL;
250
251 #if PHP_DEBUG
252 if (psi_check_env("PSI_DEBUG")) {
253 flags |= PSI_DEBUG;
254 }
255 #endif
256
257 if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS(), "S|lz", &string, &flags, &errcnt)) {
258 return;
259 }
260
261 if (!psi_parser_init(&P, psi_error_wrapper, flags)) {
262 RETURN_FALSE;
263 }
264 if (!(I = psi_parser_open_string(&P, string->val, string->len))) {
265 psi_parser_dtor(&P);
266 RETURN_FALSE;
267 }
268
269 psi_parser_parse(&P, I);
270 psi_data_ctor(&D, P.error, P.flags);
271 psi_validate_scope_ctor(&S);
272 S.cpp = P.preproc;
273
274 RETVAL_BOOL(psi_validate(&S, &D, PSI_DATA(&P)));
275
276 if (errcnt) {
277 ZVAL_DEREF(errcnt);
278 convert_to_long(errcnt);
279 ZVAL_LONG(errcnt, P.errors);
280 }
281
282 psi_validate_scope_dtor(&S);
283 psi_data_dtor(&D);
284 psi_parser_dtor(&P);
285 psi_parser_input_free(&I);
286 }
287
288 PHP_MINIT_FUNCTION(psi_cpp);
289 PHP_MINIT_FUNCTION(psi_builtin);
290 PHP_MINIT_FUNCTION(psi_context);
291 static PHP_MINIT_FUNCTION(psi)
292 {
293 zend_class_entry ce = {0};
294
295 REGISTER_INI_ENTRIES();
296
297 zend_register_long_constant(ZEND_STRL("PSI_DEBUG"), PSI_DEBUG, CONST_CS|CONST_PERSISTENT, module_number);
298 zend_register_long_constant(ZEND_STRL("PSI_SILENT"), PSI_SILENT, CONST_CS|CONST_PERSISTENT, module_number);
299
300 INIT_NS_CLASS_ENTRY(ce, "psi", "object", NULL);
301 psi_class_entry = zend_register_internal_class_ex(&ce, NULL);
302 psi_class_entry->create_object = psi_object_init;
303
304 memcpy(&psi_object_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers));
305 psi_object_handlers.offset = XtOffsetOf(psi_object, std);
306 psi_object_handlers.free_obj = psi_object_free;
307 psi_object_handlers.clone_obj = NULL;
308
309 if (SUCCESS != PHP_MINIT(psi_builtin)(type, module_number)) {
310 return FAILURE;
311 }
312 if (SUCCESS != PHP_MINIT(psi_cpp)(type, module_number)) {
313 return FAILURE;
314 }
315 if (SUCCESS != PHP_MINIT(psi_context)(type, module_number)) {
316 return FAILURE;
317 }
318
319 return SUCCESS;
320 }
321
322 PHP_MSHUTDOWN_FUNCTION(psi_cpp);
323 PHP_MSHUTDOWN_FUNCTION(psi_builtin);
324 PHP_MSHUTDOWN_FUNCTION(psi_context);
325 static PHP_MSHUTDOWN_FUNCTION(psi)
326 {
327 PHP_MSHUTDOWN(psi_context)(type, module_number);
328 PHP_MSHUTDOWN(psi_cpp)(type, module_number);
329 PHP_MSHUTDOWN(psi_builtin)(type, module_number);
330
331 UNREGISTER_INI_ENTRIES();
332
333 return SUCCESS;
334 }
335
336 #if defined(COMPILE_DL_PSI) && defined(ZTS)
337 static PHP_RINIT_FUNCTION(psi)
338 {
339 ZEND_TSRMLS_CACHE_UPDATE();
340 return SUCCESS;
341 }
342 #endif
343
344 static PHP_MINFO_FUNCTION(psi)
345 {
346 php_info_print_table_start();
347 php_info_print_table_header(2, "PSI Support", "enabled");
348 php_info_print_table_row(2, "Extension Version", PHP_PSI_VERSION);
349 php_info_print_table_row(2, "Search Path", PSI_G(search_path));
350 php_info_print_table_end();
351
352 php_info_print_table_start();
353 php_info_print_table_header(3, "Used Library", "Compiled", "Linked");
354 php_info_print_table_row(3, "libffi",
355 #ifndef PHP_PSI_LIBFFI_VERSION
356 # define PHP_PSI_LIBFFI_VERSION "unknown"
357 #endif
358 #ifdef HAVE_LIBFFI
359 PHP_PSI_LIBFFI_VERSION, "unknown"
360 #else
361 "disabled", "disabled"
362 #endif
363 );
364 php_info_print_table_row(3, "libjit",
365 #ifdef HAVE_LIBJIT
366 "unknown", "unknown"
367 #else
368 "disabled", "disabled"
369 #endif
370 );
371
372 DISPLAY_INI_ENTRIES();
373 }
374
375 static void ptr_free(void *ptr)
376 {
377 free(*(void **) ptr);
378 }
379
380 static void psi_blacklist_add_decl(const char *pattern, size_t len)
381 {
382 char *tmp = strndup(pattern, len);
383 struct psi_plist **decls = &PSI_G(blacklist).decls;
384
385 *decls = psi_plist_add(*decls, &tmp);
386 }
387
388 static void psi_blacklist_add_var(const char *pattern, size_t len)
389 {
390 char *tmp = strndup(pattern, len);
391 struct psi_plist **vars = &PSI_G(blacklist).vars;
392
393 *vars = psi_plist_add(*vars, &tmp);
394 }
395
396 static PHP_GINIT_FUNCTION(psi)
397 {
398 char *tmp;
399 struct psi_plist **bl_decls = &psi_globals->blacklist.decls;
400 struct psi_plist **bl_vars = &psi_globals->blacklist.vars;
401
402 *bl_decls = psi_plist_init(ptr_free);
403 *bl_vars = psi_plist_init(ptr_free);
404
405 #define BL_ADD(D, d) \
406 tmp = strdup(d); \
407 *D = psi_plist_add(*D, &tmp)
408 #define BL_DECL_ADD(d) BL_ADD(bl_decls, d)
409 #define BL_VAR_ADD(d) BL_ADD(bl_vars, d)
410
411 BL_DECL_ADD("dlsym");
412 BL_DECL_ADD("alloca");
413 BL_DECL_ADD("atexit");
414 BL_DECL_ADD("at_quick_exit");
415
416 /* missing */
417 BL_DECL_ADD("_IO_cookie_init");
418 BL_DECL_ADD("bindresvport6");
419
420 /* va_list as arg */
421 BL_DECL_ADD("*v*printf");
422 BL_DECL_ADD("*v*scanf");
423 BL_DECL_ADD("vsyslog");
424
425 /* LFS/LFO for 32bit */
426 BL_DECL_ADD("*stat*64");
427 BL_DECL_ADD("*glob*64");
428 /* Hurd only */
429 BL_DECL_ADD("getumask");
430
431 /* using hidden structs */
432 BL_VAR_ADD("_IO_2_*");
433 }
434
435 static PHP_GSHUTDOWN_FUNCTION(psi)
436 {
437 psi_plist_free(psi_globals->blacklist.decls);
438 }
439
440 static const zend_function_entry psi_functions[] = {
441 PHP_FE(psi_dump, ai_psi_dump)
442 PHP_FE(psi_validate, ai_psi_validate)
443 PHP_FE(psi_validate_string, ai_psi_validate_string)
444 PHP_FE_END
445 };
446
447 static const zend_module_dep psi_deps[] = {
448 ZEND_MOD_REQUIRED("standard")
449 {0}
450 };
451
452 zend_module_entry psi_module_entry = {
453 STANDARD_MODULE_HEADER_EX,
454 NULL,
455 psi_deps,
456 "psi",
457 psi_functions,
458 PHP_MINIT(psi),
459 PHP_MSHUTDOWN(psi),
460 #if defined(COMPILE_DL_PSI) && defined(ZTS)
461 PHP_RINIT(psi), /* Replace with NULL if there's nothing to do at request start */
462 #else
463 NULL,
464 #endif
465 NULL,
466 PHP_MINFO(psi),
467 PHP_PSI_VERSION,
468 ZEND_MODULE_GLOBALS(psi),
469 PHP_GINIT(psi),
470 PHP_GSHUTDOWN(psi),
471 NULL, /* post-deactivate */
472 STANDARD_MODULE_PROPERTIES_EX
473 };
474
475 #ifdef COMPILE_DL_PSI
476 #ifdef ZTS
477 ZEND_TSRMLS_CACHE_DEFINE();
478 #endif
479 ZEND_GET_MODULE(psi)
480 #endif
481
482 /*
483 * Local variables:
484 * tab-width: 4
485 * c-basic-offset: 4
486 * End:
487 * vim600: noet sw=4 ts=4 fdm=marker
488 * vim<600: noet sw=4 ts=4
489 */