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