xref: /php-src/sapi/phpdbg/phpdbg.c (revision dad2d561)
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: Felipe Pena <felipe@php.net>                                |
14    | Authors: Joe Watkins <joe.watkins@live.co.uk>                        |
15    | Authors: Bob Weinand <bwoebi@php.net>                                |
16    +----------------------------------------------------------------------+
17 */
18 
19 #include "phpdbg.h"
20 #include "phpdbg_prompt.h"
21 #include "phpdbg_bp.h"
22 #include "phpdbg_break.h"
23 #include "phpdbg_list.h"
24 #include "phpdbg_utils.h"
25 #include "phpdbg_set.h"
26 #include "phpdbg_io.h"
27 #include "zend_alloc.h"
28 #include "phpdbg_print.h"
29 #include "phpdbg_help.h"
30 #include "phpdbg_arginfo.h"
31 #include "zend_vm.h"
32 #include "php_ini_builder.h"
33 
34 #include "ext/standard/basic_functions.h"
35 
36 #if defined(PHP_WIN32) && defined(HAVE_OPENSSL)
37 # include "openssl/applink.c"
38 #endif
39 
40 #if defined(PHP_WIN32) && defined(ZTS)
41 ZEND_TSRMLS_CACHE_DEFINE()
42 #endif
43 
44 ZEND_DECLARE_MODULE_GLOBALS(phpdbg)
45 int phpdbg_startup_run = 0;
46 
47 static bool phpdbg_booted = 0;
48 static bool phpdbg_fully_started = 0;
49 bool use_mm_wrappers = 1;
50 
php_phpdbg_destroy_bp_file(zval * brake)51 static void php_phpdbg_destroy_bp_file(zval *brake) /* {{{ */
52 {
53 	zend_hash_destroy(Z_ARRVAL_P(brake));
54 	efree(Z_ARRVAL_P(brake));
55 } /* }}} */
56 
php_phpdbg_destroy_bp_symbol(zval * brake)57 static void php_phpdbg_destroy_bp_symbol(zval *brake) /* {{{ */
58 {
59 	efree((char *) ((phpdbg_breaksymbol_t *) Z_PTR_P(brake))->symbol);
60 	efree(Z_PTR_P(brake));
61 } /* }}} */
62 
php_phpdbg_destroy_bp_opcode(zval * brake)63 static void php_phpdbg_destroy_bp_opcode(zval *brake) /* {{{ */
64 {
65 	efree((char *) ((phpdbg_breakop_t *) Z_PTR_P(brake))->name);
66 	efree(Z_PTR_P(brake));
67 } /* }}} */
68 
php_phpdbg_destroy_bp_opline(zval * brake)69 static void php_phpdbg_destroy_bp_opline(zval *brake) /* {{{ */
70 {
71 	efree(Z_PTR_P(brake));
72 } /* }}} */
73 
php_phpdbg_destroy_bp_methods(zval * brake)74 static void php_phpdbg_destroy_bp_methods(zval *brake) /* {{{ */
75 {
76 	zend_hash_destroy(Z_ARRVAL_P(brake));
77 	efree(Z_ARRVAL_P(brake));
78 } /* }}} */
79 
php_phpdbg_destroy_bp_condition(zval * data)80 static void php_phpdbg_destroy_bp_condition(zval *data) /* {{{ */
81 {
82 	phpdbg_breakcond_t *brake = (phpdbg_breakcond_t *) Z_PTR_P(data);
83 
84 	if (brake->ops) {
85 		destroy_op_array(brake->ops);
86 		efree(brake->ops);
87 	}
88 	efree((char*) brake->code);
89 	efree(brake);
90 } /* }}} */
91 
php_phpdbg_destroy_registered(zval * data)92 static void php_phpdbg_destroy_registered(zval *data) /* {{{ */
93 {
94 	zend_function_dtor(data);
95 } /* }}} */
96 
php_phpdbg_destroy_file_source(zval * data)97 static void php_phpdbg_destroy_file_source(zval *data) /* {{{ */
98 {
99 	phpdbg_file_source *source = (phpdbg_file_source *) Z_PTR_P(data);
100 	destroy_op_array(&source->op_array);
101 	if (source->buf) {
102 		efree(source->buf);
103 	}
104 	efree(source);
105 } /* }}} */
106 
php_phpdbg_globals_ctor(zend_phpdbg_globals * pg)107 static inline void php_phpdbg_globals_ctor(zend_phpdbg_globals *pg) /* {{{ */
108 {
109 	pg->prompt[0] = NULL;
110 	pg->prompt[1] = NULL;
111 
112 	pg->colors[0] = NULL;
113 	pg->colors[1] = NULL;
114 	pg->colors[2] = NULL;
115 
116 	pg->lines = phpdbg_get_terminal_height();
117 	pg->exec = NULL;
118 	pg->exec_len = 0;
119 	pg->buffer = NULL;
120 	pg->last_was_newline = 1;
121 	pg->ops = NULL;
122 	pg->vmret = 0;
123 	pg->in_execution = 0;
124 	pg->bp_count = 0;
125 	pg->flags = PHPDBG_DEFAULT_FLAGS;
126 	memset(pg->io, 0, sizeof(pg->io));
127 	pg->frame.num = 0;
128 	pg->sapi_name_ptr = NULL;
129 	pg->unclean_eval = 0;
130 
131 	pg->req_id = 0;
132 	pg->err_buf.active = 0;
133 	pg->err_buf.type = 0;
134 
135 	pg->input_buflen = 0;
136 	pg->sigsafe_mem.mem = NULL;
137 	pg->sigsegv_bailout = NULL;
138 
139 	pg->oplog_list = NULL;
140 	pg->stdin_file = NULL;
141 
142 	pg->cur_command = NULL;
143 	pg->last_line = 0;
144 
145 #ifdef HAVE_USERFAULTFD_WRITEFAULT
146 	pg->watch_userfaultfd = 0;
147 	pg->watch_userfault_thread = 0;
148 #endif
149 } /* }}} */
150 
PHP_MINIT_FUNCTION(phpdbg)151 static PHP_MINIT_FUNCTION(phpdbg) /* {{{ */
152 {
153 	zend_hash_init(&PHPDBG_G(bp)[PHPDBG_BREAK_FILE], 8, NULL, php_phpdbg_destroy_bp_file, 0);
154 	zend_hash_init(&PHPDBG_G(bp)[PHPDBG_BREAK_FILE_PENDING], 8, NULL, php_phpdbg_destroy_bp_file, 0);
155 	zend_hash_init(&PHPDBG_G(bp)[PHPDBG_BREAK_SYM], 8, NULL, php_phpdbg_destroy_bp_symbol, 0);
156 	zend_hash_init(&PHPDBG_G(bp)[PHPDBG_BREAK_FUNCTION_OPLINE], 8, NULL, php_phpdbg_destroy_bp_methods, 0);
157 	zend_hash_init(&PHPDBG_G(bp)[PHPDBG_BREAK_METHOD_OPLINE], 8, NULL, php_phpdbg_destroy_bp_methods, 0);
158 	zend_hash_init(&PHPDBG_G(bp)[PHPDBG_BREAK_FILE_OPLINE], 8, NULL, php_phpdbg_destroy_bp_methods, 0);
159 	zend_hash_init(&PHPDBG_G(bp)[PHPDBG_BREAK_OPLINE], 8, NULL, php_phpdbg_destroy_bp_opline, 0);
160 	zend_hash_init(&PHPDBG_G(bp)[PHPDBG_BREAK_OPCODE], 8, NULL, php_phpdbg_destroy_bp_opcode, 0);
161 	zend_hash_init(&PHPDBG_G(bp)[PHPDBG_BREAK_METHOD], 8, NULL, php_phpdbg_destroy_bp_methods, 0);
162 	zend_hash_init(&PHPDBG_G(bp)[PHPDBG_BREAK_COND], 8, NULL, php_phpdbg_destroy_bp_condition, 0);
163 	zend_hash_init(&PHPDBG_G(bp)[PHPDBG_BREAK_MAP], 8, NULL, NULL, 0);
164 
165 	zend_hash_init(&PHPDBG_G(seek), 8, NULL, NULL, 0);
166 	zend_hash_init(&PHPDBG_G(registered), 8, NULL, php_phpdbg_destroy_registered, 0);
167 
168 	zend_hash_init(&PHPDBG_G(file_sources), 0, NULL, php_phpdbg_destroy_file_source, 0);
169 	phpdbg_setup_watchpoints();
170 
171 	zend_execute_ex = phpdbg_execute_ex;
172 
173 	register_phpdbg_symbols(module_number);
174 
175 	return SUCCESS;
176 } /* }}} */
177 
PHP_MSHUTDOWN_FUNCTION(phpdbg)178 static PHP_MSHUTDOWN_FUNCTION(phpdbg) /* {{{ */
179 {
180 	zend_hash_destroy(&PHPDBG_G(registered));
181 	phpdbg_destroy_watchpoints();
182 
183 	if (!(PHPDBG_G(flags) & PHPDBG_IS_QUITTING)) {
184 		phpdbg_notice("Script ended normally");
185 	}
186 
187 	/* hack to restore mm_heap->use_custom_heap in order to receive memory leak info */
188 	if (use_mm_wrappers) {
189 		/* ASSUMING that mm_heap->use_custom_heap is the first element of the struct ... */
190 		*(int *) zend_mm_get_heap() = 0;
191 	}
192 
193 	if (PHPDBG_G(buffer)) {
194 		free(PHPDBG_G(buffer));
195 		PHPDBG_G(buffer) = NULL;
196 	}
197 
198 	if (PHPDBG_G(exec)) {
199 		free(PHPDBG_G(exec));
200 		PHPDBG_G(exec) = NULL;
201 	}
202 
203 	if (PHPDBG_G(oplog_list)) {
204 		phpdbg_oplog_list *cur = PHPDBG_G(oplog_list);
205 		do {
206 			phpdbg_oplog_list *prev = cur->prev;
207 			efree(cur);
208 			cur = prev;
209 		} while (cur != NULL);
210 
211 		zend_arena_destroy(PHPDBG_G(oplog_arena));
212 		PHPDBG_G(oplog_list) = NULL;
213 	}
214 
215 	fflush(stdout);
216 	if (SG(request_info).argv0) {
217 		free(SG(request_info).argv0);
218 		SG(request_info).argv0 = NULL;
219 	}
220 
221 	return SUCCESS;
222 }
223 /* }}} */
224 
PHP_RINIT_FUNCTION(phpdbg)225 static PHP_RINIT_FUNCTION(phpdbg) /* {{{ */
226 {
227 	/* deactivate symbol table caching to have these properly destroyed upon stack leaving (especially important for watchpoints) */
228 	EG(symtable_cache_limit) = EG(symtable_cache);
229 
230 	if (zend_vm_kind() != ZEND_VM_KIND_HYBRID) {
231 		/* phpdbg cannot work JIT-ed code */
232 		zend_string *key = zend_string_init(ZEND_STRL("opcache.jit"), 1);
233 		zend_string *value = zend_string_init(ZEND_STRL("off"), 1);
234 
235 		zend_alter_ini_entry(key, value, ZEND_INI_SYSTEM, ZEND_INI_STAGE_STARTUP);
236 
237 		zend_string_release(key);
238 		zend_string_release(value);
239 	}
240 
241 	return SUCCESS;
242 } /* }}} */
243 
PHP_RSHUTDOWN_FUNCTION(phpdbg)244 static PHP_RSHUTDOWN_FUNCTION(phpdbg) /* {{{ */
245 {
246 	if (PHPDBG_G(stdin_file)) {
247 		fclose(PHPDBG_G(stdin_file));
248 		PHPDBG_G(stdin_file) = NULL;
249 	}
250 
251 	return SUCCESS;
252 } /* }}} */
253 
254 /* {{{ Attempt to set the execution context for phpdbg
255 	If the execution context was set previously it is returned
256 	If the execution context was not set previously boolean true is returned
257 	If the request to set the context fails, boolean false is returned, and an E_WARNING raised */
PHP_FUNCTION(phpdbg_exec)258 PHP_FUNCTION(phpdbg_exec)
259 {
260 	zend_string *exec;
261 
262 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "S", &exec) == FAILURE) {
263 		RETURN_THROWS();
264 	}
265 
266 	{
267 		zend_stat_t sb = {0};
268 		bool result = 1;
269 
270 		if (VCWD_STAT(ZSTR_VAL(exec), &sb) != FAILURE) {
271 			if (sb.st_mode & (S_IFREG|S_IFLNK)) {
272 				if (PHPDBG_G(exec)) {
273 					ZVAL_STRINGL(return_value, PHPDBG_G(exec), PHPDBG_G(exec_len));
274 					free(PHPDBG_G(exec));
275 					result = 0;
276 				}
277 
278 				PHPDBG_G(exec) = zend_strndup(ZSTR_VAL(exec), ZSTR_LEN(exec));
279 				PHPDBG_G(exec_len) = ZSTR_LEN(exec);
280 
281 				if (result) {
282 					ZVAL_TRUE(return_value);
283 				}
284 			} else {
285 				zend_error(E_WARNING, "Failed to set execution context (%s), not a regular file or symlink", ZSTR_VAL(exec));
286 				ZVAL_FALSE(return_value);
287 			}
288 		} else {
289 			zend_error(E_WARNING, "Failed to set execution context (%s) the file does not exist", ZSTR_VAL(exec));
290 
291 			ZVAL_FALSE(return_value);
292 		}
293 	}
294 } /* }}} */
295 
296 /* {{{ instructs phpdbg to insert a breakpoint at the next opcode */
PHP_FUNCTION(phpdbg_break_next)297 PHP_FUNCTION(phpdbg_break_next)
298 {
299 	zend_execute_data *ex;
300 
301 	if (zend_parse_parameters_none() == FAILURE) {
302 		RETURN_THROWS();
303 	}
304 
305 	ex = EG(current_execute_data);
306 	while (ex && ex->func && !ZEND_USER_CODE(ex->func->type)) {
307 		ex = ex->prev_execute_data;
308 	}
309 
310 	if (!ex) {
311 		return;
312 	}
313 
314 	phpdbg_set_breakpoint_opline_ex((phpdbg_opline_ptr_t) ex->opline + 1);
315 } /* }}} */
316 
317 /* {{{ */
PHP_FUNCTION(phpdbg_break_file)318 PHP_FUNCTION(phpdbg_break_file)
319 {
320 	char *file;
321 	size_t flen;
322 	zend_long line;
323 
324 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "sl", &file, &flen, &line) == FAILURE) {
325 		RETURN_THROWS();
326 	}
327 
328 	phpdbg_set_breakpoint_file(file, 0, line);
329 } /* }}} */
330 
331 /* {{{ */
PHP_FUNCTION(phpdbg_break_method)332 PHP_FUNCTION(phpdbg_break_method)
333 {
334 	char *class, *method;
335 	size_t clen, mlen;
336 
337 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "ss", &class, &clen, &method, &mlen) == FAILURE) {
338 		RETURN_THROWS();
339 	}
340 
341 	phpdbg_set_breakpoint_method(class, method);
342 } /* }}} */
343 
344 /* {{{ */
PHP_FUNCTION(phpdbg_break_function)345 PHP_FUNCTION(phpdbg_break_function)
346 {
347 	char    *function;
348 	size_t   function_len;
349 
350 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &function, &function_len) == FAILURE) {
351 		RETURN_THROWS();
352 	}
353 
354 	phpdbg_set_breakpoint_symbol(function, function_len);
355 } /* }}} */
356 
357 /* {{{ instructs phpdbg to clear breakpoints */
PHP_FUNCTION(phpdbg_clear)358 PHP_FUNCTION(phpdbg_clear)
359 {
360 	if (zend_parse_parameters_none() == FAILURE) {
361 		RETURN_THROWS();
362 	}
363 
364 	zend_hash_clean(&PHPDBG_G(bp)[PHPDBG_BREAK_FILE]);
365 	zend_hash_clean(&PHPDBG_G(bp)[PHPDBG_BREAK_FILE_PENDING]);
366 	zend_hash_clean(&PHPDBG_G(bp)[PHPDBG_BREAK_SYM]);
367 	zend_hash_clean(&PHPDBG_G(bp)[PHPDBG_BREAK_FUNCTION_OPLINE]);
368 	zend_hash_clean(&PHPDBG_G(bp)[PHPDBG_BREAK_METHOD_OPLINE]);
369 	zend_hash_clean(&PHPDBG_G(bp)[PHPDBG_BREAK_FILE_OPLINE]);
370 	zend_hash_clean(&PHPDBG_G(bp)[PHPDBG_BREAK_OPLINE]);
371 	zend_hash_clean(&PHPDBG_G(bp)[PHPDBG_BREAK_METHOD]);
372 	zend_hash_clean(&PHPDBG_G(bp)[PHPDBG_BREAK_COND]);
373 } /* }}} */
374 
375 /* {{{ */
PHP_FUNCTION(phpdbg_color)376 PHP_FUNCTION(phpdbg_color)
377 {
378 	zend_long element;
379 	char *color;
380 	size_t color_len;
381 
382 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "ls", &element, &color, &color_len) == FAILURE) {
383 		RETURN_THROWS();
384 	}
385 
386 	switch (element) {
387 		case PHPDBG_COLOR_NOTICE:
388 		case PHPDBG_COLOR_ERROR:
389 		case PHPDBG_COLOR_PROMPT:
390 			phpdbg_set_color_ex(element, color, color_len);
391 		break;
392 
393 		default:
394 			zend_argument_value_error(1, "must be one of PHPDBG_COLOR_PROMPT, PHPDBG_COLOR_NOTICE, or PHPDBG_COLOR_ERROR");
395 	}
396 } /* }}} */
397 
398 /* {{{ */
PHP_FUNCTION(phpdbg_prompt)399 PHP_FUNCTION(phpdbg_prompt)
400 {
401 	char *prompt = NULL;
402 	size_t prompt_len = 0;
403 
404 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &prompt, &prompt_len) == FAILURE) {
405 		RETURN_THROWS();
406 	}
407 
408 	phpdbg_set_prompt(prompt);
409 } /* }}} */
410 
411 /* {{{ */
PHP_FUNCTION(phpdbg_start_oplog)412 PHP_FUNCTION(phpdbg_start_oplog)
413 {
414 	phpdbg_oplog_list *prev;
415 
416 	if (zend_parse_parameters_none() == FAILURE) {
417 		RETURN_THROWS();
418 	}
419 
420 	prev = PHPDBG_G(oplog_list);
421 
422 	if (!prev) {
423 		PHPDBG_G(oplog_arena) = zend_arena_create(64 * 1024);
424 	}
425 
426 	PHPDBG_G(oplog_list) = emalloc(sizeof(phpdbg_oplog_list));
427 	PHPDBG_G(oplog_list)->prev = prev;
428 	PHPDBG_G(oplog_cur) = &PHPDBG_G(oplog_list)->start;
429 	PHPDBG_G(oplog_cur)->next = NULL;
430 }
431 
phpdbg_is_ignored_opcode(uint8_t opcode)432 static zend_always_inline bool phpdbg_is_ignored_opcode(uint8_t opcode) {
433 	return
434 	    opcode == ZEND_NOP || opcode == ZEND_OP_DATA || opcode == ZEND_FE_FREE || opcode == ZEND_FREE || opcode == ZEND_ASSERT_CHECK || opcode == ZEND_VERIFY_RETURN_TYPE
435 	 || opcode == ZEND_DECLARE_CONST || opcode == ZEND_DECLARE_CLASS || opcode == ZEND_DECLARE_FUNCTION
436 	 || opcode == ZEND_DECLARE_CLASS_DELAYED
437 	 || opcode == ZEND_DECLARE_ANON_CLASS || opcode == ZEND_FAST_RET || opcode == ZEND_TICKS
438 	 || opcode == ZEND_EXT_STMT || opcode == ZEND_EXT_FCALL_BEGIN || opcode == ZEND_EXT_FCALL_END
439 	 || opcode == ZEND_BIND_GLOBAL || opcode == ZEND_BIND_INIT_STATIC_OR_JMP
440 	;
441 }
442 
phpdbg_oplog_fill_executable(zend_op_array * op_array,HashTable * insert_ht,bool by_opcode)443 static void phpdbg_oplog_fill_executable(zend_op_array *op_array, HashTable *insert_ht, bool by_opcode) {
444 	/* ignore RECV_* opcodes */
445 	zend_op *cur = op_array->opcodes + op_array->num_args + !!(op_array->fn_flags & ZEND_ACC_VARIADIC);
446 	zend_op *end = op_array->opcodes + op_array->last;
447 
448 	zend_long insert_idx;
449 	zval zero;
450 	ZVAL_LONG(&zero, 0);
451 
452 	/* ignore autogenerated return (well, not too precise with finally branches, but that's okay) */
453 	if (op_array->last >= 1 && (((end - 1)->opcode == ZEND_RETURN || (end - 1)->opcode == ZEND_RETURN_BY_REF || (end - 1)->opcode == ZEND_GENERATOR_RETURN)
454 	 && ((op_array->last > 1 && ((end - 2)->opcode == ZEND_RETURN || (end - 2)->opcode == ZEND_RETURN_BY_REF || (end - 2)->opcode == ZEND_GENERATOR_RETURN || (end - 2)->opcode == ZEND_THROW))
455 	  || op_array->function_name == NULL || (end - 1)->extended_value == -1))) {
456 		end--;
457 	}
458 
459 	for (; cur < end; cur++) {
460 		uint8_t opcode = cur->opcode;
461 		if (phpdbg_is_ignored_opcode(opcode)) {
462 			continue;
463 		}
464 
465 		if (by_opcode) {
466 			insert_idx = cur - op_array->opcodes;
467 		} else {
468 			insert_idx = cur->lineno;
469 		}
470 
471 		if (opcode == ZEND_NEW && cur[1].opcode == ZEND_DO_FCALL) {
472 			cur++;
473 		}
474 
475 		zend_hash_index_update(insert_ht, insert_idx, &zero);
476 	}
477 }
478 
phpdbg_add_empty_array(HashTable * ht,zend_string * name)479 static inline HashTable* phpdbg_add_empty_array(HashTable *ht, zend_string *name) {
480 	zval *ht_zv = zend_hash_find(ht, name);
481 	if (!ht_zv) {
482 		zval zv;
483 		array_init(&zv);
484 		ht_zv = zend_hash_add_new(ht, name, &zv);
485 	}
486 	return Z_ARR_P(ht_zv);
487 }
488 
489 /* {{{ */
PHP_FUNCTION(phpdbg_get_executable)490 PHP_FUNCTION(phpdbg_get_executable)
491 {
492 	HashTable *options = NULL;
493 	zval *option_buffer;
494 	bool by_function = 0;
495 	bool by_opcode = 0;
496 	HashTable *insert_ht;
497 
498 	zend_function *func;
499 	zend_class_entry *ce;
500 	zend_string *name;
501 	HashTable *files = &PHPDBG_G(file_sources);
502 	HashTable files_tmp;
503 
504 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "|H", &options) == FAILURE) {
505 		RETURN_THROWS();
506 	}
507 
508 	if (options && (option_buffer = zend_hash_str_find(options, ZEND_STRL("functions")))) {
509 		by_function = zend_is_true(option_buffer);
510 	}
511 
512 	if (options && (option_buffer = zend_hash_str_find(options, ZEND_STRL("opcodes")))) {
513 		if (by_function) {
514 			by_opcode = zend_is_true(option_buffer);
515 		}
516 	}
517 
518 	if (options && (option_buffer = zend_hash_str_find(options, ZEND_STRL("files")))) {
519 		ZVAL_DEREF(option_buffer);
520 		if (Z_TYPE_P(option_buffer) == IS_ARRAY && zend_hash_num_elements(Z_ARR_P(option_buffer)) > 0) {
521 			zval *filename;
522 
523 			files = &files_tmp;
524 			zend_hash_init(files, 0, NULL, NULL, 0);
525 
526 			ZEND_HASH_FOREACH_VAL(Z_ARR_P(option_buffer), filename) {
527 				zend_hash_add_empty_element(files, zval_get_string(filename));
528 			} ZEND_HASH_FOREACH_END();
529 		} else {
530 			GC_ADDREF(files);
531 		}
532 	} else {
533 		GC_ADDREF(files);
534 	}
535 
536 	array_init(return_value);
537 
538 	ZEND_HASH_MAP_FOREACH_STR_KEY_PTR(EG(function_table), name, func) {
539 		if (func->type == ZEND_USER_FUNCTION) {
540 			if (zend_hash_exists(files, func->op_array.filename)) {
541 				insert_ht = phpdbg_add_empty_array(Z_ARR_P(return_value), func->op_array.filename);
542 
543 				if (by_function) {
544 					insert_ht = phpdbg_add_empty_array(insert_ht, name);
545 				}
546 
547 				phpdbg_oplog_fill_executable(&func->op_array, insert_ht, by_opcode);
548 			}
549 		}
550 	} ZEND_HASH_FOREACH_END();
551 
552 	ZEND_HASH_MAP_FOREACH_STR_KEY_PTR(EG(class_table), name, ce) {
553 		if (ce->type == ZEND_USER_CLASS) {
554 			if (zend_hash_exists(files, ce->info.user.filename)) {
555 				ZEND_HASH_MAP_FOREACH_PTR(&ce->function_table, func) {
556 					if (func->type == ZEND_USER_FUNCTION && zend_hash_exists(files, func->op_array.filename)) {
557 						insert_ht = phpdbg_add_empty_array(Z_ARR_P(return_value), func->op_array.filename);
558 
559 						if (by_function) {
560 							zend_string *fn_name = strpprintf(ZSTR_LEN(name) + ZSTR_LEN(func->op_array.function_name) + 2, "%.*s::%.*s", (int) ZSTR_LEN(name), ZSTR_VAL(name), (int) ZSTR_LEN(func->op_array.function_name), ZSTR_VAL(func->op_array.function_name));
561 							insert_ht = phpdbg_add_empty_array(insert_ht, fn_name);
562 							zend_string_release(fn_name);
563 						}
564 
565 						phpdbg_oplog_fill_executable(&func->op_array, insert_ht, by_opcode);
566 					}
567 				} ZEND_HASH_FOREACH_END();
568 			}
569 		}
570 	} ZEND_HASH_FOREACH_END();
571 
572 	ZEND_HASH_MAP_FOREACH_STR_KEY(files, name) {
573 		phpdbg_file_source *source = zend_hash_find_ptr(&PHPDBG_G(file_sources), name);
574 		if (source) {
575 			phpdbg_oplog_fill_executable(
576 				&source->op_array,
577 				phpdbg_add_empty_array(Z_ARR_P(return_value), source->op_array.filename),
578 				by_opcode);
579 		}
580 	} ZEND_HASH_FOREACH_END();
581 
582 	if (!GC_DELREF(files)) {
583 		zend_hash_destroy(files);
584 	}
585 }
586 
587 /* {{{ */
PHP_FUNCTION(phpdbg_end_oplog)588 PHP_FUNCTION(phpdbg_end_oplog)
589 {
590 	phpdbg_oplog_entry *cur;
591 	phpdbg_oplog_list *prev;
592 
593 	HashTable *options = NULL;
594 	zval *option_buffer;
595 	bool by_function = 0;
596 	bool by_opcode = 0;
597 
598 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "|H", &options) == FAILURE) {
599 		RETURN_THROWS();
600 	}
601 
602 	if (!PHPDBG_G(oplog_list)) {
603 		zend_error(E_WARNING, "Cannot end an oplog without starting it");
604 		return;
605 	}
606 
607 	cur = PHPDBG_G(oplog_list)->start.next;
608 	prev = PHPDBG_G(oplog_list)->prev;
609 
610 	efree(PHPDBG_G(oplog_list));
611 	PHPDBG_G(oplog_list) = prev;
612 
613 	if (options && (option_buffer = zend_hash_str_find(options, ZEND_STRL("functions")))) {
614 		by_function = zend_is_true(option_buffer);
615 	}
616 
617 	if (options && (option_buffer = zend_hash_str_find(options, ZEND_STRL("opcodes")))) {
618 		if (by_function) {
619 			by_opcode = zend_is_true(option_buffer);
620 		}
621 	}
622 
623 	array_init(return_value);
624 
625 	{
626 		zend_string *last_file = NULL;
627 		HashTable *file_ht = NULL;
628 		zend_string *last_function = (void *)~(uintptr_t)0;
629 		zend_class_entry *last_scope = NULL;
630 
631 		HashTable *insert_ht = NULL;
632 		zend_long insert_idx;
633 
634 		do {
635 			zval zero;
636 			ZVAL_LONG(&zero, 0);
637 
638 			if (cur->filename != last_file) {
639 				last_file = cur->filename;
640 				file_ht = insert_ht = phpdbg_add_empty_array(Z_ARR_P(return_value), last_file);
641 			}
642 
643 			if (by_function) {
644 				if (cur->function_name == NULL) {
645 					if (last_function != NULL) {
646 						insert_ht = file_ht;
647 					}
648 					last_function = NULL;
649 				} else if (cur->function_name != last_function || cur->scope != last_scope) {
650 					zend_string *fn_name;
651 					last_function = cur->function_name;
652 					last_scope = cur->scope;
653 					if (last_scope == NULL) {
654 						fn_name = zend_string_copy(last_function);
655 					} else {
656 						fn_name = strpprintf(ZSTR_LEN(last_function) + ZSTR_LEN(last_scope->name) + 2, "%.*s::%.*s", (int) ZSTR_LEN(last_scope->name), ZSTR_VAL(last_scope->name), (int) ZSTR_LEN(last_function), ZSTR_VAL(last_function));
657 					}
658 					insert_ht = phpdbg_add_empty_array(Z_ARR_P(return_value), fn_name);
659 					zend_string_release(fn_name);
660 				}
661 			}
662 
663 			if (by_opcode) {
664 				insert_idx = cur->op - cur->opcodes;
665 			} else {
666 				if (phpdbg_is_ignored_opcode(cur->op->opcode)) {
667 					continue;
668 				}
669 
670 				insert_idx = cur->op->lineno;
671 			}
672 
673 			ZEND_ASSERT(insert_ht && file_ht);
674 			{
675 				zval *num = zend_hash_index_find(insert_ht, insert_idx);
676 				if (!num) {
677 					num = zend_hash_index_add_new(insert_ht, insert_idx, &zero);
678 				}
679 				Z_LVAL_P(num)++;
680 			}
681 
682 		} while ((cur = cur->next));
683 	}
684 
685 	if (!prev) {
686 		zend_arena_destroy(PHPDBG_G(oplog_arena));
687 	}
688 }
689 
690 static zend_module_entry sapi_phpdbg_module_entry = {
691 	STANDARD_MODULE_HEADER,
692 	PHPDBG_NAME,
693 	ext_functions,
694 	PHP_MINIT(phpdbg),
695 	PHP_MSHUTDOWN(phpdbg),
696 	PHP_RINIT(phpdbg),
697 	PHP_RSHUTDOWN(phpdbg),
698 	NULL,
699 	PHPDBG_VERSION,
700 	STANDARD_MODULE_PROPERTIES
701 };
702 
php_sapi_phpdbg_module_startup(sapi_module_struct * module)703 static inline int php_sapi_phpdbg_module_startup(sapi_module_struct *module) /* {{{ */
704 {
705 	if (php_module_startup(module, &sapi_phpdbg_module_entry) == FAILURE) {
706 		return FAILURE;
707 	}
708 
709 	phpdbg_booted = 1;
710 
711 	return SUCCESS;
712 } /* }}} */
713 
php_sapi_phpdbg_read_cookies(void)714 static char* php_sapi_phpdbg_read_cookies(void) /* {{{ */
715 {
716 	return NULL;
717 } /* }}} */
718 
php_sapi_phpdbg_header_handler(sapi_header_struct * h,sapi_header_op_enum op,sapi_headers_struct * s)719 static int php_sapi_phpdbg_header_handler(sapi_header_struct *h, sapi_header_op_enum op, sapi_headers_struct *s) /* {{{ */
720 {
721 	return 0;
722 }
723 /* }}} */
724 
php_sapi_phpdbg_send_headers(sapi_headers_struct * sapi_headers)725 static int php_sapi_phpdbg_send_headers(sapi_headers_struct *sapi_headers) /* {{{ */
726 {
727 	/* We do nothing here, this function is needed to prevent that the fallback
728 	 * header handling is called. */
729 	return SAPI_HEADER_SENT_SUCCESSFULLY;
730 }
731 /* }}} */
732 
php_sapi_phpdbg_send_header(sapi_header_struct * sapi_header,void * server_context)733 static void php_sapi_phpdbg_send_header(sapi_header_struct *sapi_header, void *server_context) /* {{{ */
734 {
735 }
736 /* }}} */
737 
php_sapi_phpdbg_log_message(const char * message,int syslog_type_int)738 static void php_sapi_phpdbg_log_message(const char *message, int syslog_type_int) /* {{{ */
739 {
740 	/*
741 	* We must not request TSRM before being booted
742 	*/
743 	if (phpdbg_booted) {
744 		if (PHPDBG_G(flags) & PHPDBG_IN_EVAL) {
745 			phpdbg_error("%s", message);
746 			return;
747 		}
748 
749 		phpdbg_error("%s", message);
750 
751 		if (PHPDBG_G(flags) & PHPDBG_PREVENT_INTERACTIVE) {
752 			return;
753 		}
754 
755 		if (PG(last_error_type) & E_FATAL_ERRORS) {
756 			const char *file_char = zend_get_executed_filename();
757 			zend_string *file = zend_string_init(file_char, strlen(file_char), 0);
758 			phpdbg_list_file(file, 3, zend_get_executed_lineno() - 1, zend_get_executed_lineno());
759 			zend_string_release(file);
760 
761 			if (!phpdbg_fully_started) {
762 				return;
763 			}
764 
765 			do {
766 				switch (phpdbg_interactive(1, NULL)) {
767 					case PHPDBG_LEAVE:
768 					case PHPDBG_FINISH:
769 					case PHPDBG_UNTIL:
770 					case PHPDBG_NEXT:
771 						return;
772 				}
773 			} while (!(PHPDBG_G(flags) & PHPDBG_IS_STOPPING));
774 		}
775 	} else {
776 		fprintf(stdout, "%s\n", message);
777 	}
778 }
779 /* }}} */
780 
php_sapi_phpdbg_activate(void)781 static int php_sapi_phpdbg_activate(void) /* {{{ */
782 {
783 	return SUCCESS;
784 }
785 
php_sapi_phpdbg_deactivate(void)786 static int php_sapi_phpdbg_deactivate(void) /* {{{ */
787 {
788 	/* Everything using ZMM should be freed here... */
789 	zend_hash_destroy(&PHPDBG_G(file_sources));
790 	zend_hash_destroy(&PHPDBG_G(bp)[PHPDBG_BREAK_FILE]);
791 	zend_hash_destroy(&PHPDBG_G(bp)[PHPDBG_BREAK_FILE_PENDING]);
792 	zend_hash_destroy(&PHPDBG_G(bp)[PHPDBG_BREAK_SYM]);
793 	zend_hash_destroy(&PHPDBG_G(bp)[PHPDBG_BREAK_FUNCTION_OPLINE]);
794 	zend_hash_destroy(&PHPDBG_G(bp)[PHPDBG_BREAK_METHOD_OPLINE]);
795 	zend_hash_destroy(&PHPDBG_G(bp)[PHPDBG_BREAK_FILE_OPLINE]);
796 	zend_hash_destroy(&PHPDBG_G(bp)[PHPDBG_BREAK_OPLINE]);
797 	zend_hash_destroy(&PHPDBG_G(bp)[PHPDBG_BREAK_OPCODE]);
798 	zend_hash_destroy(&PHPDBG_G(bp)[PHPDBG_BREAK_METHOD]);
799 	zend_hash_destroy(&PHPDBG_G(bp)[PHPDBG_BREAK_COND]);
800 	zend_hash_destroy(&PHPDBG_G(bp)[PHPDBG_BREAK_MAP]);
801 	zend_hash_destroy(&PHPDBG_G(seek));
802 
803 	if (PHPDBG_G(ops)) {
804 		destroy_op_array(PHPDBG_G(ops));
805 		efree(PHPDBG_G(ops));
806 		PHPDBG_G(ops) = NULL;
807 	}
808 
809 	return SUCCESS;
810 }
811 
php_sapi_phpdbg_register_vars(zval * track_vars_array)812 static void php_sapi_phpdbg_register_vars(zval *track_vars_array) /* {{{ */
813 {
814 	size_t len;
815 	char  *docroot = "";
816 
817 	/* In phpdbg mode, we consider the environment to be a part of the server variables
818 	*/
819 	php_import_environment_variables(track_vars_array);
820 
821 	if (PHPDBG_G(exec)) {
822 		len = PHPDBG_G(exec_len);
823 		if (sapi_module.input_filter(PARSE_SERVER, "PHP_SELF", &PHPDBG_G(exec), PHPDBG_G(exec_len), &len)) {
824 			php_register_variable("PHP_SELF", PHPDBG_G(exec), track_vars_array);
825 		}
826 		if (sapi_module.input_filter(PARSE_SERVER, "SCRIPT_NAME", &PHPDBG_G(exec), PHPDBG_G(exec_len), &len)) {
827 			php_register_variable("SCRIPT_NAME", PHPDBG_G(exec), track_vars_array);
828 		}
829 
830 		if (sapi_module.input_filter(PARSE_SERVER, "SCRIPT_FILENAME", &PHPDBG_G(exec), PHPDBG_G(exec_len), &len)) {
831 			php_register_variable("SCRIPT_FILENAME", PHPDBG_G(exec), track_vars_array);
832 		}
833 		if (sapi_module.input_filter(PARSE_SERVER, "PATH_TRANSLATED", &PHPDBG_G(exec), PHPDBG_G(exec_len), &len)) {
834 			php_register_variable("PATH_TRANSLATED", PHPDBG_G(exec), track_vars_array);
835 		}
836 	}
837 
838 	/* any old docroot will do */
839 	len = 0;
840 	if (sapi_module.input_filter(PARSE_SERVER, "DOCUMENT_ROOT", &docroot, len, &len)) {
841 		php_register_variable("DOCUMENT_ROOT", docroot, track_vars_array);
842 	}
843 }
844 /* }}} */
845 
php_sapi_phpdbg_ub_write(const char * message,size_t length)846 static inline size_t php_sapi_phpdbg_ub_write(const char *message, size_t length) /* {{{ */
847 {
848 	return phpdbg_script(P_STDOUT, "%.*s", (int) length, message);
849 } /* }}} */
850 
851 /* beginning of struct, see main/streams/plain_wrapper.c line 111 */
852 typedef struct {
853 	FILE *file;
854 	int fd;
855 } php_stdio_stream_data;
856 
phpdbg_stdiop_write(php_stream * stream,const char * buf,size_t count)857 static ssize_t phpdbg_stdiop_write(php_stream *stream, const char *buf, size_t count) {
858 	php_stdio_stream_data *data = (php_stdio_stream_data*)stream->abstract;
859 
860 	while (data->fd >= 0) {
861 		struct stat stat[3];
862 		memset(stat, 0, sizeof(stat));
863 		int stat_stderr = fstat(fileno(stderr), &stat[2]);
864 		int stat_stdout = fstat(fileno(stdout), &stat[0]);
865 		int stat_datafd = fstat(data->fd, &stat[1]);
866 		if ((stat_stderr < 0 && stat_stdout < 0) || stat_datafd < 0) {
867 			break;
868 		}
869 
870 		if (stat[0].st_dev == stat[1].st_dev && stat[0].st_ino == stat[1].st_ino) {
871 			phpdbg_script(P_STDOUT, "%.*s", (int) count, buf);
872 			return count;
873 		}
874 		if (stat[2].st_dev == stat[1].st_dev && stat[2].st_ino == stat[1].st_ino) {
875 			phpdbg_script(P_STDERR, "%.*s", (int) count, buf);
876 			return count;
877 		}
878 		break;
879 	}
880 
881 	return PHPDBG_G(php_stdiop_write)(stream, buf, count);
882 }
883 
884 /* copied from sapi/cli/php_cli.c cli_register_file_handles */
phpdbg_register_file_handles(void)885 void phpdbg_register_file_handles(void) /* {{{ */
886 {
887 	zval zin, zout, zerr;
888 	php_stream *s_in, *s_out, *s_err;
889 	php_stream_context *sc_in=NULL, *sc_out=NULL, *sc_err=NULL;
890 	zend_constant ic, oc, ec;
891 
892 	s_in  = php_stream_open_wrapper_ex("php://stdin",  "rb", 0, NULL, sc_in);
893 	s_out = php_stream_open_wrapper_ex("php://stdout", "wb", 0, NULL, sc_out);
894 	s_err = php_stream_open_wrapper_ex("php://stderr", "wb", 0, NULL, sc_err);
895 
896 	if (s_in==NULL || s_out==NULL || s_err==NULL) {
897 		if (s_in) php_stream_close(s_in);
898 		if (s_out) php_stream_close(s_out);
899 		if (s_err) php_stream_close(s_err);
900 		return;
901 	}
902 
903 #if PHP_DEBUG
904 	/* do not close stdout and stderr */
905 	s_out->flags |= PHP_STREAM_FLAG_NO_CLOSE;
906 	s_err->flags |= PHP_STREAM_FLAG_NO_CLOSE;
907 #endif
908 
909 	php_stream_to_zval(s_in,  &zin);
910 	php_stream_to_zval(s_out, &zout);
911 	php_stream_to_zval(s_err, &zerr);
912 
913 	ic.value = zin;
914 	Z_CONSTANT_FLAGS(ic.value) = 0;
915 	ic.name = zend_string_init(ZEND_STRL("STDIN"), 0);
916 	zend_hash_del(EG(zend_constants), ic.name);
917 	zend_register_constant(&ic);
918 
919 	oc.value = zout;
920 	Z_CONSTANT_FLAGS(oc.value) = 0;
921 	oc.name = zend_string_init(ZEND_STRL("STDOUT"), 0);
922 	zend_hash_del(EG(zend_constants), oc.name);
923 	zend_register_constant(&oc);
924 
925 	ec.value = zerr;
926 	Z_CONSTANT_FLAGS(ec.value) = 0;
927 	ec.name = zend_string_init(ZEND_STRL("STDERR"), 0);
928 	zend_hash_del(EG(zend_constants), ec.name);
929 	zend_register_constant(&ec);
930 }
931 /* }}} */
932 
933 /* {{{ sapi_module_struct phpdbg_sapi_module */
934 static sapi_module_struct phpdbg_sapi_module = {
935 	"phpdbg",                       /* name */
936 	"phpdbg",                       /* pretty name */
937 
938 	php_sapi_phpdbg_module_startup, /* startup */
939 	php_module_shutdown_wrapper,    /* shutdown */
940 
941 	php_sapi_phpdbg_activate,       /* activate */
942 	php_sapi_phpdbg_deactivate,     /* deactivate */
943 
944 	php_sapi_phpdbg_ub_write,       /* unbuffered write */
945 	NULL,                           /* flush */
946 	NULL,                           /* get uid */
947 	NULL,                           /* getenv */
948 
949 	php_error,                      /* error handler */
950 
951 	php_sapi_phpdbg_header_handler, /* header handler */
952 	php_sapi_phpdbg_send_headers,   /* send headers handler */
953 	php_sapi_phpdbg_send_header,    /* send header handler */
954 
955 	NULL,                           /* read POST data */
956 	php_sapi_phpdbg_read_cookies,   /* read Cookies */
957 
958 	php_sapi_phpdbg_register_vars,  /* register server variables */
959 	php_sapi_phpdbg_log_message,    /* Log message */
960 	NULL,                           /* Get request time */
961 	NULL,                           /* Child terminate */
962 	STANDARD_SAPI_MODULE_PROPERTIES
963 };
964 /* }}} */
965 
966 static const opt_struct OPTIONS[] = { /* {{{ */
967 	{'c', 1, "ini path override"},
968 	{'d', 1, "define ini entry on command line"},
969 	{'n', 0, "no php.ini"},
970 	{'z', 1, "load zend_extension"},
971 	/* phpdbg options */
972 	{'q', 0, "no banner"},
973 	{'v', 0, "disable quietness"},
974 	{'b', 0, "boring colours"},
975 	{'i', 1, "specify init"},
976 	{'I', 0, "ignore init"},
977 	{'O', 1, "opline log"},
978 	{'r', 0, "run"},
979 	{'e', 0, "generate ext_stmt opcodes"},
980 	{'E', 0, "step-through-eval"},
981 	{'s', 1, "script from stdin"},
982 	{'S', 1, "sapi-name"},
983 	{'p', 2, "show opcodes"},
984 	{'h', 0, "help"},
985 	{'V', 0, "version"},
986 	{'-', 0, NULL}
987 }; /* }}} */
988 
989 const char phpdbg_ini_hardcoded[] =
990 "html_errors=Off\n"
991 "register_argc_argv=On\n"
992 "implicit_flush=On\n"
993 "display_errors=Off\n"
994 "log_errors=On\n"
995 "max_execution_time=0\n"
996 "max_input_time=-1\n"
997 "error_log=\n"
998 "output_buffering=off\n";
999 
phpdbg_welcome(bool cleaning)1000 static void phpdbg_welcome(bool cleaning) /* {{{ */
1001 {
1002 	/* print blurb */
1003 	if (!cleaning) {
1004 		phpdbg_notice("Welcome to phpdbg, the interactive PHP debugger, v%s", PHPDBG_VERSION);
1005 		phpdbg_writeln("To get help using phpdbg type \"help\" and press enter");
1006 		phpdbg_notice("Please report bugs to <%s>", PHPDBG_ISSUES);
1007 	} else if (phpdbg_startup_run == 0) {
1008 		phpdbg_write(
1009 			"Classes              %d\n"
1010 			"Functions            %d\n"
1011 			"Constants            %d\n"
1012 			"Includes             %d\n",
1013 			zend_hash_num_elements(EG(class_table)),
1014 			zend_hash_num_elements(EG(function_table)),
1015 			zend_hash_num_elements(EG(zend_constants)),
1016 			zend_hash_num_elements(&EG(included_files)));
1017 	}
1018 } /* }}} */
1019 
phpdbg_sigint_handler(int signo)1020 static inline void phpdbg_sigint_handler(int signo) /* {{{ */
1021 {
1022 	if (!(PHPDBG_G(flags) & PHPDBG_IS_INTERACTIVE)) {
1023 		/* set signalled only when not interactive */
1024 		if (PHPDBG_G(flags) & PHPDBG_IS_SIGNALED) {
1025 			char mem[PHPDBG_SIGSAFE_MEM_SIZE + 1];
1026 
1027 			phpdbg_set_sigsafe_mem(mem);
1028 			zend_try {
1029 				phpdbg_force_interruption();
1030 			} zend_end_try()
1031 			phpdbg_clear_sigsafe_mem();
1032 
1033 			PHPDBG_G(flags) &= ~PHPDBG_IS_SIGNALED;
1034 
1035 			if (PHPDBG_G(flags) & PHPDBG_IS_STOPPING) {
1036 				zend_bailout();
1037 			}
1038 		} else {
1039 			PHPDBG_G(flags) |= PHPDBG_IS_SIGNALED;
1040 			if (PHPDBG_G(flags) & PHPDBG_PREVENT_INTERACTIVE) {
1041 				PHPDBG_G(flags) |= PHPDBG_HAS_PAGINATION;
1042 				PHPDBG_G(flags) &= ~PHPDBG_PREVENT_INTERACTIVE;
1043 			}
1044 		}
1045 	}
1046 } /* }}} */
1047 
1048 #ifndef _WIN32
phpdbg_signal_handler(int sig,siginfo_t * info,void * context)1049 void phpdbg_signal_handler(int sig, siginfo_t *info, void *context) /* {{{ */
1050 {
1051 	int is_handled = FAILURE;
1052 
1053 	switch (sig) {
1054 		case SIGBUS:
1055 		case SIGSEGV:
1056 			is_handled = phpdbg_watchpoint_segfault_handler(info, context);
1057 			if (is_handled == FAILURE) {
1058 				if (PHPDBG_G(sigsegv_bailout)) {
1059 					LONGJMP(*PHPDBG_G(sigsegv_bailout), FAILURE);
1060 				}
1061 				zend_sigaction(sig, &PHPDBG_G(old_sigsegv_signal), NULL);
1062 			}
1063 			break;
1064 	}
1065 
1066 } /* }}} */
1067 
1068 
phpdbg_sighup_handler(int sig)1069 ZEND_NORETURN void phpdbg_sighup_handler(int sig) /* {{{ */
1070 {
1071 	exit(0);
1072 } /* }}} */
1073 #endif
1074 
phpdbg_malloc_wrapper(size_t size ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC)1075 ZEND_ATTRIBUTE_MALLOC ZEND_ATTRIBUTE_ALLOC_SIZE(1) void *phpdbg_malloc_wrapper(size_t size ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC) /* {{{ */
1076 {
1077 	return _zend_mm_alloc(zend_mm_get_heap(), size ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC);
1078 } /* }}} */
1079 
phpdbg_free_wrapper(void * p ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC)1080 void phpdbg_free_wrapper(void *p ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC) /* {{{ */
1081 {
1082 	zend_mm_heap *heap = zend_mm_get_heap();
1083 	if (UNEXPECTED(heap == p)) {
1084 		/* TODO: heap maybe allocated by mmap(zend_mm_init) or malloc(USE_ZEND_ALLOC=0)
1085 		 * let's prevent it from segfault for now
1086 		 */
1087 	} else {
1088 		phpdbg_watch_efree(p ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC);
1089 		_zend_mm_free(heap, p ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC);
1090 	}
1091 } /* }}} */
1092 
phpdbg_realloc_wrapper(void * ptr,size_t size ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC)1093 void *phpdbg_realloc_wrapper(void *ptr, size_t size ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC) /* {{{ */
1094 {
1095 	return _zend_mm_realloc(zend_mm_get_heap(), ptr, size ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC);
1096 } /* }}} */
1097 
phpdbg_stream_url_wrap_php(php_stream_wrapper * wrapper,const char * path,const char * mode,int options,zend_string ** opened_path,php_stream_context * context STREAMS_DC)1098 php_stream *phpdbg_stream_url_wrap_php(php_stream_wrapper *wrapper, const char *path, const char *mode, int options, zend_string **opened_path, php_stream_context *context STREAMS_DC) /* {{{ */
1099 {
1100 	if (!strncasecmp(path, "php://", 6)) {
1101 		path += 6;
1102 	}
1103 
1104 	if (!strncasecmp(path, "stdin", 6) && PHPDBG_G(stdin_file)) {
1105 		php_stream *stream = php_stream_fopen_from_fd(dup(fileno(PHPDBG_G(stdin_file))), "r", NULL);
1106 #ifdef PHP_WIN32
1107 		if (context != NULL) {
1108 			zval *blocking_pipes = php_stream_context_get_option(context, "pipe", "blocking");
1109 			if (blocking_pipes) {
1110 				convert_to_long(blocking_pipes);
1111 				php_stream_set_option(stream, PHP_STREAM_OPTION_PIPE_BLOCKING, Z_LVAL_P(blocking_pipes), NULL);
1112 			}
1113 		}
1114 #endif
1115 		return stream;
1116 	}
1117 
1118 	return PHPDBG_G(orig_url_wrap_php)->wops->stream_opener(wrapper, path, mode, options, opened_path, context STREAMS_CC);
1119 } /* }}} */
1120 
main(int argc,char ** argv)1121 int main(int argc, char **argv) /* {{{ */
1122 {
1123 	sapi_module_struct *phpdbg = &phpdbg_sapi_module;
1124 	char *sapi_name;
1125 	struct php_ini_builder ini_builder;
1126 	char **zend_extensions = NULL;
1127 	zend_ulong zend_extensions_len = 0L;
1128 	bool ini_ignore;
1129 	char *ini_override;
1130 	char *exec = NULL;
1131 	char *first_command = NULL;
1132 	char *init_file;
1133 	size_t init_file_len;
1134 	bool init_file_default;
1135 	uint64_t flags;
1136 	char *php_optarg;
1137 	int php_optind, opt, show_banner = 1;
1138 	long cleaning = -1;
1139 	volatile bool quit_immediately = 0; /* somehow some gcc release builds will play a bit around with order in combination with setjmp..., hence volatile */
1140 	zend_phpdbg_globals *settings = NULL;
1141 	char *bp_tmp = NULL;
1142 	char *print_opline_func;
1143 	bool ext_stmt = 0;
1144 	bool is_exit;
1145 	int exit_status;
1146 	char *read_from_stdin = NULL;
1147 	zend_string *backup_phpdbg_compile = NULL;
1148 	bool show_help = 0, show_version = 0;
1149 	void* (*_malloc)(size_t ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC);
1150 	void (*_free)(void* ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC);
1151 	void* (*_realloc)(void*, size_t ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC);
1152 	php_stream_wrapper wrapper;
1153 	php_stream_wrapper_ops wops;
1154 
1155 #ifdef PHP_WIN32
1156 	_fmode = _O_BINARY;                 /* sets default for file streams to binary */
1157 	setmode(_fileno(stdin), O_BINARY);  /* make the stdio mode be binary */
1158 	setmode(_fileno(stdout), O_BINARY); /* make the stdio mode be binary */
1159 	setmode(_fileno(stderr), O_BINARY); /* make the stdio mode be binary */
1160 #else
1161 	struct sigaction signal_struct;
1162 	signal_struct.sa_sigaction = phpdbg_signal_handler;
1163 	signal_struct.sa_flags = SA_SIGINFO | SA_NODEFER;
1164 #endif
1165 
1166 phpdbg_main:
1167 #ifdef ZTS
1168 	php_tsrm_startup();
1169 # ifdef PHP_WIN32
1170 	ZEND_TSRMLS_CACHE_UPDATE();
1171 # endif
1172 #endif
1173 
1174 	zend_signal_startup();
1175 
1176 	php_ini_builder_init(&ini_builder);
1177 	ini_ignore = 0;
1178 	ini_override = NULL;
1179 	zend_extensions = NULL;
1180 	zend_extensions_len = 0L;
1181 	init_file = NULL;
1182 	init_file_len = 0;
1183 	init_file_default = 1;
1184 	flags = PHPDBG_DEFAULT_FLAGS;
1185 	is_exit = 0;
1186 	php_optarg = NULL;
1187 	php_optind = 1;
1188 	opt = 0;
1189 	sapi_name = NULL;
1190 	exit_status = 0;
1191 	if (settings) {
1192 		exec = settings->exec;
1193 	}
1194 
1195 	while ((opt = php_getopt(argc, argv, OPTIONS, &php_optarg, &php_optind, 0, 2)) != -1) {
1196 		switch (opt) {
1197 			case 'r':
1198 				if (settings == NULL) {
1199 					phpdbg_startup_run++;
1200 				}
1201 				break;
1202 			case 'n':
1203 				ini_ignore = 1;
1204 				break;
1205 			case 'c':
1206 				if (ini_override) {
1207 					free(ini_override);
1208 				}
1209 				ini_override = strdup(php_optarg);
1210 				break;
1211 			case 'd':
1212 				/* define ini entries on command line */
1213 				php_ini_builder_define(&ini_builder, php_optarg);
1214 				break;
1215 
1216 			case 'z':
1217 				zend_extensions_len++;
1218 				if (zend_extensions) {
1219 					zend_extensions = realloc(zend_extensions, sizeof(char*) * zend_extensions_len);
1220 				} else zend_extensions = malloc(sizeof(char*) * zend_extensions_len);
1221 				zend_extensions[zend_extensions_len-1] = strdup(php_optarg);
1222 			break;
1223 
1224 			/* begin phpdbg options */
1225 
1226 			case 's': { /* read script from stdin */
1227 				if (settings == NULL) {
1228 					read_from_stdin = strdup(php_optarg);
1229 				}
1230 			} break;
1231 
1232 			case 'S': { /* set SAPI name */
1233 				sapi_name = strdup(php_optarg);
1234 			} break;
1235 
1236 			case 'I': { /* ignore .phpdbginit */
1237 				init_file_default = 0;
1238 			} break;
1239 
1240 			case 'i': { /* set init file */
1241 				if (init_file) {
1242 					free(init_file);
1243 					init_file = NULL;
1244 				}
1245 
1246 				init_file_len = strlen(php_optarg);
1247 				if (init_file_len) {
1248 					init_file = strdup(php_optarg);
1249 				}
1250 			} break;
1251 
1252 			case 'v': /* set quietness off */
1253 				flags &= ~PHPDBG_IS_QUIET;
1254 			break;
1255 
1256 			case 'e':
1257 				ext_stmt = 1;
1258 			break;
1259 
1260 			case 'E': /* stepping through eval on */
1261 				flags |= PHPDBG_IS_STEPONEVAL;
1262 			break;
1263 
1264 			case 'b': /* set colours off */
1265 				flags &= ~PHPDBG_IS_COLOURED;
1266 			break;
1267 
1268 			case 'q': /* hide banner */
1269 				show_banner = 0;
1270 			break;
1271 
1272 			case 'p': {
1273 				print_opline_func = php_optarg;
1274 				show_banner = 0;
1275 				settings = (void *) 0x1;
1276 			} break;
1277 
1278 			case 'h': {
1279 				show_help = 1;
1280 			} break;
1281 
1282 			case 'V': {
1283 				show_version = 1;
1284 			} break;
1285 		}
1286 
1287 		php_optarg = NULL;
1288 	}
1289 
1290 	quit_immediately = phpdbg_startup_run > 1;
1291 
1292 	/* set exec if present on command line */
1293 	if (!read_from_stdin && argc > php_optind) {
1294 		if (!exec && strlen(argv[php_optind])) {
1295 			exec = strdup(argv[php_optind]);
1296 		}
1297 		php_optind++;
1298 	}
1299 
1300 	if (sapi_name) {
1301 		phpdbg->name = sapi_name;
1302 	}
1303 
1304 	phpdbg->ini_defaults = NULL;
1305 	phpdbg->phpinfo_as_text = 1;
1306 	phpdbg->php_ini_ignore_cwd = 1;
1307 
1308 	sapi_startup(phpdbg);
1309 
1310 	phpdbg->executable_location = argv[0];
1311 	phpdbg->phpinfo_as_text = 1;
1312 	phpdbg->php_ini_ignore = ini_ignore;
1313 	phpdbg->php_ini_path_override = ini_override;
1314 
1315 	php_ini_builder_prepend_literal(&ini_builder, phpdbg_ini_hardcoded);
1316 
1317 	if (zend_extensions_len) {
1318 		zend_ulong zend_extension = 0L;
1319 
1320 		while (zend_extension < zend_extensions_len) {
1321 			const char *ze = zend_extensions[zend_extension];
1322 			size_t ze_len = strlen(ze);
1323 
1324 			php_ini_builder_unquoted(&ini_builder, "zend_extension", strlen("zend_extension"), ze, ze_len);
1325 
1326 			free(zend_extensions[zend_extension]);
1327 			zend_extension++;
1328 		}
1329 
1330 		free(zend_extensions);
1331 	}
1332 
1333 	phpdbg->ini_entries = php_ini_builder_finish(&ini_builder);
1334 
1335 	ZEND_INIT_MODULE_GLOBALS(phpdbg, php_phpdbg_globals_ctor, NULL);
1336 
1337 	/* set default colors */
1338 	phpdbg_set_color_ex(PHPDBG_COLOR_PROMPT,  PHPDBG_STRL("white-bold"));
1339 	phpdbg_set_color_ex(PHPDBG_COLOR_ERROR,   PHPDBG_STRL("red-bold"));
1340 	phpdbg_set_color_ex(PHPDBG_COLOR_NOTICE,  PHPDBG_STRL("green"));
1341 
1342 	if (settings > (zend_phpdbg_globals *) 0x2) {
1343 #ifdef ZTS
1344 		zend_phpdbg_globals *ptr = TSRMG_BULK_STATIC(phpdbg_globals_id, zend_phpdbg_globals *);
1345 		*ptr = *settings;
1346 #else
1347 		phpdbg_globals = *settings;
1348 #endif
1349 		free(settings);
1350 	} else {
1351 		/* set default prompt */
1352 		phpdbg_set_prompt(PHPDBG_DEFAULT_PROMPT);
1353 	}
1354 
1355 	/* set flags from command line */
1356 	PHPDBG_G(flags) = flags;
1357 
1358 	if (phpdbg->startup(phpdbg) == SUCCESS) {
1359 		zend_mm_heap *mm_heap;
1360 #ifdef _WIN32
1361 	EXCEPTION_POINTERS *xp;
1362 	__try {
1363 #endif
1364 
1365 		if (show_version || show_help) {
1366 			/* It ain't gonna proceed to real execution anyway,
1367 				but the correct descriptor is needed already. */
1368 			PHPDBG_G(io)[PHPDBG_STDOUT].fd = fileno(stdout);
1369 			if (show_help) {
1370 				phpdbg_do_help_cmd(exec);
1371 			} else if (show_version) {
1372 				phpdbg_out(
1373 					"phpdbg %s (built: %s %s)\nPHP %s, Copyright (c) The PHP Group\n%s",
1374 					PHPDBG_VERSION,
1375 					__DATE__,
1376 					__TIME__,
1377 					PHP_VERSION,
1378 					get_zend_version()
1379 				);
1380 			}
1381 			PHPDBG_G(flags) |= PHPDBG_IS_QUITTING;
1382 			php_module_shutdown();
1383 			sapi_deactivate();
1384 			sapi_shutdown();
1385 			php_ini_builder_deinit(&ini_builder);
1386 			if (ini_override) {
1387 				free(ini_override);
1388 			}
1389 			if (exec) {
1390 				free(exec);
1391 			}
1392 			if (init_file) {
1393 				free(init_file);
1394 			}
1395 			goto free_and_return;
1396 		}
1397 
1398 		zend_try {
1399 			zend_signal_activate();
1400 		} zend_end_try();
1401 
1402 #ifndef _WIN32
1403 		zend_signal(SIGHUP, phpdbg_sighup_handler);
1404 #endif
1405 
1406 		mm_heap = zend_mm_get_heap();
1407 		zend_mm_get_custom_handlers(mm_heap, &_malloc, &_free, &_realloc);
1408 
1409 		use_mm_wrappers = !_malloc && !_realloc && !_free;
1410 
1411 		PHPDBG_G(original_free_function) = _free;
1412 		_free = phpdbg_watch_efree;
1413 
1414 		if (use_mm_wrappers) {
1415 			zend_mm_set_custom_handlers(mm_heap, phpdbg_malloc_wrapper, phpdbg_free_wrapper, phpdbg_realloc_wrapper);
1416 		} else {
1417 			zend_mm_set_custom_handlers(mm_heap, _malloc, _free, _realloc);
1418 		}
1419 
1420 		_free = PHPDBG_G(original_free_function);
1421 
1422 
1423 		phpdbg_init_list();
1424 
1425 		PHPDBG_G(sapi_name_ptr) = sapi_name;
1426 
1427 		if (exec) { /* set execution context */
1428 			PHPDBG_G(exec) = phpdbg_resolve_path(exec);
1429 			PHPDBG_G(exec_len) = PHPDBG_G(exec) ? strlen(PHPDBG_G(exec)) : 0;
1430 
1431 			free(exec);
1432 			exec = NULL;
1433 		}
1434 
1435 		php_output_activate();
1436 		php_output_deactivate();
1437 
1438 		if (SG(sapi_headers).mimetype) {
1439 			efree(SG(sapi_headers).mimetype);
1440 			SG(sapi_headers).mimetype = NULL;
1441 		}
1442 
1443 		php_output_activate();
1444 
1445 		{
1446 			int i;
1447 
1448 			SG(request_info).argc = argc - php_optind + 1;
1449 			SG(request_info).argv = emalloc(SG(request_info).argc * sizeof(char *));
1450 			for (i = SG(request_info).argc; --i;) {
1451 				SG(request_info).argv[i] = estrdup(argv[php_optind - 1 + i]);
1452 			}
1453 			SG(request_info).argv[0] = PHPDBG_G(exec) ? estrdup(PHPDBG_G(exec)) : estrdup("");
1454 		}
1455 
1456 		if (php_request_startup() == FAILURE) {
1457 			PUTS("Could not startup");
1458 			return 1;
1459 		}
1460 
1461 #ifndef _WIN32
1462 #ifdef HAVE_USERFAULTFD_WRITEFAULT
1463 		if (!PHPDBG_G(watch_userfaultfd))
1464 #endif
1465 		{
1466 			zend_try { zend_sigaction(SIGSEGV, &signal_struct, &PHPDBG_G(old_sigsegv_signal)); } zend_end_try();
1467 			zend_try { zend_sigaction(SIGBUS, &signal_struct, &PHPDBG_G(old_sigsegv_signal)); } zend_end_try();
1468 		}
1469 #endif
1470 		zend_try { zend_signal(SIGINT, phpdbg_sigint_handler); } zend_end_try();
1471 
1472 
1473 		PHPDBG_G(io)[PHPDBG_STDIN].fd = fileno(stdin);
1474 		PHPDBG_G(io)[PHPDBG_STDOUT].fd = fileno(stdout);
1475 		PHPDBG_G(io)[PHPDBG_STDERR].fd = fileno(stderr);
1476 
1477 #ifndef _WIN32
1478 		PHPDBG_G(php_stdiop_write) = php_stream_stdio_ops.write;
1479 		php_stream_stdio_ops.write = phpdbg_stdiop_write;
1480 #endif
1481 
1482 		{
1483 			zval *zv = zend_hash_str_find(php_stream_get_url_stream_wrappers_hash(), ZEND_STRL("php"));
1484 			php_stream_wrapper *tmp_wrapper = Z_PTR_P(zv);
1485 			PHPDBG_G(orig_url_wrap_php) = tmp_wrapper;
1486 			memcpy(&wrapper, tmp_wrapper, sizeof(wrapper));
1487 			memcpy(&wops, tmp_wrapper->wops, sizeof(wops));
1488 			wops.stream_opener = phpdbg_stream_url_wrap_php;
1489 			wrapper.wops = (const php_stream_wrapper_ops*)&wops;
1490 			Z_PTR_P(zv) = &wrapper;
1491 		}
1492 
1493 		/* Make stdin, stdout and stderr accessible from PHP scripts */
1494 		phpdbg_register_file_handles();
1495 
1496 		phpdbg_list_update();
1497 
1498 		if (show_banner && cleaning < 2) {
1499 			/* print blurb */
1500 			phpdbg_welcome(cleaning == 1);
1501 		}
1502 
1503 		cleaning = -1;
1504 
1505 		if (ext_stmt) {
1506 			CG(compiler_options) |= ZEND_COMPILE_EXTENDED_INFO;
1507 		}
1508 
1509 		/* initialize from file */
1510 		PHPDBG_G(flags) |= PHPDBG_IS_INITIALIZING;
1511 		zend_try {
1512 			phpdbg_init(init_file, init_file_len, init_file_default);
1513 		} zend_end_try();
1514 		PHPDBG_G(flags) &= ~PHPDBG_IS_INITIALIZING;
1515 
1516 		/* quit if init says so */
1517 		if (PHPDBG_G(flags) & PHPDBG_IS_QUITTING) {
1518 			goto phpdbg_out;
1519 		}
1520 
1521 		/* auto compile */
1522 		if (read_from_stdin) {
1523 			if (!read_from_stdin[0]) {
1524 				if (!quit_immediately) {
1525 					phpdbg_error("Impossible to not specify a stdin delimiter without -rr");
1526 					PHPDBG_G(flags) |= PHPDBG_IS_QUITTING;
1527 					goto phpdbg_out;
1528 				}
1529 			}
1530 			if (show_banner || read_from_stdin[0]) {
1531 				phpdbg_notice("Reading input from stdin; put '%s' followed by a newline on an own line after code to end input", read_from_stdin);
1532 			}
1533 
1534 			if (phpdbg_startup_run > 0) {
1535 				PHPDBG_G(flags) |= PHPDBG_DISCARD_OUTPUT;
1536 			}
1537 
1538 			zend_try {
1539 				phpdbg_param_t cmd;
1540 				cmd.str = read_from_stdin;
1541 				cmd.len = strlen(read_from_stdin);
1542 				PHPDBG_COMMAND_HANDLER(stdin)(&cmd);
1543 			} zend_end_try();
1544 
1545 			PHPDBG_G(flags) &= ~PHPDBG_DISCARD_OUTPUT;
1546 		} else if (PHPDBG_G(exec)) {
1547 			if (settings || phpdbg_startup_run > 0) {
1548 				PHPDBG_G(flags) |= PHPDBG_DISCARD_OUTPUT;
1549 			}
1550 
1551 			zend_try {
1552 				if (backup_phpdbg_compile) {
1553 					phpdbg_compile_stdin(backup_phpdbg_compile);
1554 				} else {
1555 					phpdbg_compile();
1556 				}
1557 			} zend_end_try();
1558 			backup_phpdbg_compile = NULL;
1559 
1560 			PHPDBG_G(flags) &= ~PHPDBG_DISCARD_OUTPUT;
1561 		}
1562 
1563 		if (bp_tmp) {
1564 			PHPDBG_G(flags) |= PHPDBG_DISCARD_OUTPUT | PHPDBG_IS_INITIALIZING;
1565 			phpdbg_string_init(bp_tmp);
1566 			free(bp_tmp);
1567 			bp_tmp = NULL;
1568 			PHPDBG_G(flags) &= ~PHPDBG_DISCARD_OUTPUT & ~PHPDBG_IS_INITIALIZING;
1569 		}
1570 
1571 		if (settings == (void *) 0x1) {
1572 			if (PHPDBG_G(ops)) {
1573 				phpdbg_print_opcodes(print_opline_func);
1574 			} else {
1575 				zend_quiet_write(PHPDBG_G(io)[PHPDBG_STDERR].fd, ZEND_STRL("No opcodes could be compiled | No file specified or compilation failed?\n"));
1576 			}
1577 			goto phpdbg_out;
1578 		}
1579 
1580 		PG(during_request_startup) = 0;
1581 
1582 		phpdbg_fully_started = 1;
1583 
1584 		/* phpdbg main() */
1585 		do {
1586 			zend_try {
1587 				if (phpdbg_startup_run) {
1588 					phpdbg_startup_run = 0;
1589 					if (quit_immediately) {
1590 						PHPDBG_G(flags) = (PHPDBG_G(flags) & ~PHPDBG_HAS_PAGINATION) | PHPDBG_IS_INTERACTIVE | PHPDBG_PREVENT_INTERACTIVE;
1591 					} else {
1592 						PHPDBG_G(flags) |= PHPDBG_IS_INTERACTIVE;
1593 					}
1594 					zend_try {
1595 						if (first_command) {
1596 							phpdbg_interactive(1, estrdup(first_command));
1597 						} else {
1598 							PHPDBG_COMMAND_HANDLER(run)(NULL);
1599 						}
1600 					} zend_end_try();
1601 					if (quit_immediately) {
1602 						/* if -r is on the command line more than once just quit */
1603 						EG(bailout) = __orig_bailout; /* reset zend_try */
1604 						exit_status = EG(exit_status);
1605 						break;
1606 					}
1607 				}
1608 
1609 				CG(unclean_shutdown) = 0;
1610 				phpdbg_interactive(1, NULL);
1611 			} zend_catch {
1612 				if ((PHPDBG_G(flags) & PHPDBG_IS_CLEANING)) {
1613 					char *bp_tmp_str;
1614 					PHPDBG_G(flags) |= PHPDBG_DISCARD_OUTPUT;
1615 					phpdbg_export_breakpoints_to_string(&bp_tmp_str);
1616 					PHPDBG_G(flags) &= ~PHPDBG_DISCARD_OUTPUT;
1617 					if (bp_tmp_str) {
1618 						bp_tmp = strdup(bp_tmp_str);
1619 						free(bp_tmp_str);
1620 					}
1621 					cleaning = 1;
1622 				} else {
1623 					cleaning = 0;
1624 				}
1625 			} zend_end_try();
1626 		} while (!(PHPDBG_G(flags) & PHPDBG_IS_STOPPING));
1627 
1628 #ifdef _WIN32
1629 	} __except(phpdbg_exception_handler_win32(xp = GetExceptionInformation())) {
1630 		phpdbg_error("Access violation (Segmentation fault) encountered\ntrying to abort cleanly...");
1631 	}
1632 #endif
1633 phpdbg_out:
1634 
1635 		phpdbg_purge_watchpoint_tree();
1636 
1637 		if (first_command) {
1638 			free(first_command);
1639 			first_command = NULL;
1640 		}
1641 
1642 		if (cleaning <= 0) {
1643 			PHPDBG_G(flags) &= ~PHPDBG_IS_CLEANING;
1644 			cleaning = -1;
1645 		}
1646 
1647 		{
1648 			int i;
1649 			/* free argv */
1650 			for (i = SG(request_info).argc; i--;) {
1651 				efree(SG(request_info).argv[i]);
1652 			}
1653 			efree(SG(request_info).argv);
1654 		}
1655 
1656 		php_ini_builder_deinit(&ini_builder);
1657 
1658 		if (ini_override) {
1659 			free(ini_override);
1660 		}
1661 
1662 		/* In case we aborted during script execution, we may not reset CG(unclean_shutdown) */
1663 		if (!(PHPDBG_G(flags) & PHPDBG_IS_RUNNING)) {
1664 			is_exit = !PHPDBG_G(in_execution);
1665 			CG(unclean_shutdown) = is_exit || PHPDBG_G(unclean_eval);
1666 		}
1667 
1668 		if ((PHPDBG_G(flags) & (PHPDBG_IS_CLEANING | PHPDBG_IS_RUNNING)) == PHPDBG_IS_CLEANING) {
1669 			php_free_shutdown_functions();
1670 			zend_objects_store_mark_destructed(&EG(objects_store));
1671 		}
1672 
1673 		if (PHPDBG_G(exec) && strcmp("Standard input code", PHPDBG_G(exec)) == SUCCESS) { /* i.e. execution context has been read from stdin - back it up */
1674 			phpdbg_file_source *data = zend_hash_str_find_ptr(&PHPDBG_G(file_sources), PHPDBG_G(exec), PHPDBG_G(exec_len));
1675 			backup_phpdbg_compile = zend_string_alloc(data->len + 2, 1);
1676 			GC_MAKE_PERSISTENT_LOCAL(backup_phpdbg_compile);
1677 			sprintf(ZSTR_VAL(backup_phpdbg_compile), "?>%.*s", (int) data->len, data->buf);
1678 		}
1679 
1680 		zend_try {
1681 			php_request_shutdown(NULL);
1682 		} zend_end_try();
1683 
1684 		/* backup globals when cleaning */
1685 		if ((cleaning > 0) && !quit_immediately) {
1686 			settings = calloc(1, sizeof(zend_phpdbg_globals));
1687 
1688 			php_phpdbg_globals_ctor(settings);
1689 
1690 			if (PHPDBG_G(exec)) {
1691 				settings->exec = zend_strndup(PHPDBG_G(exec), PHPDBG_G(exec_len));
1692 				settings->exec_len = PHPDBG_G(exec_len);
1693 			}
1694 			settings->prompt[0] = PHPDBG_G(prompt)[0];
1695 			settings->prompt[1] = PHPDBG_G(prompt)[1];
1696 			memcpy(ZEND_VOIDP(settings->colors), PHPDBG_G(colors), sizeof(settings->colors));
1697 			settings->input_buflen = PHPDBG_G(input_buflen);
1698 			memcpy(settings->input_buffer, PHPDBG_G(input_buffer), settings->input_buflen);
1699 			settings->flags = PHPDBG_G(flags) & PHPDBG_PRESERVE_FLAGS_MASK;
1700 			first_command = PHPDBG_G(cur_command);
1701 		} else {
1702 			if (PHPDBG_G(prompt)[0]) {
1703 				free(PHPDBG_G(prompt)[0]);
1704 			}
1705 			if (PHPDBG_G(prompt)[1]) {
1706 				free(PHPDBG_G(prompt)[1]);
1707 			}
1708 			if (PHPDBG_G(cur_command)) {
1709 				free(PHPDBG_G(cur_command));
1710 			}
1711 		}
1712 
1713 		if (exit_status == 0) {
1714 			exit_status = EG(exit_status);
1715 		}
1716 
1717 		php_output_deactivate();
1718 
1719 		if (!(PHPDBG_G(flags) & PHPDBG_IS_QUITTING)) {
1720 			PHPDBG_G(flags) |= PHPDBG_IS_QUITTING;
1721 			if (PHPDBG_G(in_execution) || is_exit) {
1722 				if (!quit_immediately && !phpdbg_startup_run) {
1723 					PHPDBG_G(flags) -= PHPDBG_IS_QUITTING;
1724 					cleaning++;
1725 				}
1726 			}
1727 		}
1728 
1729 		{
1730 			zval *zv = zend_hash_str_find(php_stream_get_url_stream_wrappers_hash(), ZEND_STRL("php"));
1731 			Z_PTR_P(zv) = (void*)PHPDBG_G(orig_url_wrap_php);
1732 		}
1733 
1734 #ifndef _WIN32
1735 		/* force override (no zend_signals) to prevent crashes due to signal recursion in SIGSEGV/SIGBUS handlers */
1736 		signal(SIGSEGV, SIG_DFL);
1737 		signal(SIGBUS, SIG_DFL);
1738 
1739 		/* reset it... else we risk a stack overflow upon next run (when clean'ing) */
1740 		php_stream_stdio_ops.write = PHPDBG_G(php_stdiop_write);
1741 #endif
1742 	}
1743 
1744 	php_module_shutdown();
1745 
1746 	sapi_shutdown();
1747 
1748 	if (sapi_name) {
1749 		free(sapi_name);
1750 	}
1751 
1752 free_and_return:
1753 	if (read_from_stdin) {
1754 		free(read_from_stdin);
1755 		read_from_stdin = NULL;
1756 	}
1757 
1758 #ifdef ZTS
1759 	/* reset to original handlers - otherwise PHPDBG_G() in phpdbg_watch_efree will be segfaulty (with e.g. USE_ZEND_ALLOC=0) */
1760 	if (!use_mm_wrappers) {
1761 		zend_mm_set_custom_handlers(zend_mm_get_heap(), _malloc, _free, _realloc);
1762 	}
1763 
1764 	ts_free_id(phpdbg_globals_id);
1765 
1766 	tsrm_shutdown();
1767 #endif
1768 
1769 	if ((cleaning > 0) && !quit_immediately) {
1770 		/* reset internal php_getopt state */
1771 		php_getopt(-1, argv, OPTIONS, NULL, &php_optind, 0, 0);
1772 
1773 		goto phpdbg_main;
1774 	}
1775 
1776 	if (backup_phpdbg_compile) {
1777 		zend_string_free(backup_phpdbg_compile);
1778 	}
1779 
1780 	/* usually 0; just for -rr */
1781 	return exit_status;
1782 } /* }}} */
1783