From: Bob Weinand Date: Thu, 2 Oct 2014 23:29:41 +0000 (+0200) Subject: Made some functions async safe and provide interactive shell in signal handler X-Git-Tag: php-5.6.3RC1~51^2~69 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=8af0870d727bf4030923aff3a13dfc7e3ceaaec5;p=php Made some functions async safe and provide interactive shell in signal handler Fixes also (as a side-effect) a possible bug in list function when included files become unreachable after loading by caching the file contents This is already pretty stable; a (restricted) ev and sh may follow. --- diff --git a/phpdbg.c b/phpdbg.c index a72ddbb183..15647f2c58 100644 --- a/phpdbg.c +++ b/phpdbg.c @@ -74,6 +74,7 @@ static inline void php_phpdbg_globals_ctor(zend_phpdbg_globals *pg) /* {{{ */ pg->frame.num = 0; pg->input_buflen = 0; pg->sigsafe_mem.mem = NULL; + pg->sigsegv_bailout = NULL; } /* }}} */ static PHP_MINIT_FUNCTION(phpdbg) /* {{{ */ @@ -181,6 +182,7 @@ static PHP_RSHUTDOWN_FUNCTION(phpdbg) /* {{{ */ zend_hash_destroy(&PHPDBG_G(bp)[PHPDBG_BREAK_MAP]); zend_hash_destroy(&PHPDBG_G(seek)); zend_hash_destroy(&PHPDBG_G(registered)); + zend_hash_destroy(&PHPDBG_G(file_sources)); zend_hash_destroy(&PHPDBG_G(watchpoints)); zend_llist_destroy(&PHPDBG_G(watchlist_mem)); @@ -188,7 +190,7 @@ static PHP_RSHUTDOWN_FUNCTION(phpdbg) /* {{{ */ efree(PHPDBG_G(buffer)); PHPDBG_G(buffer) = NULL; } - + if (PHPDBG_G(exec)) { efree(PHPDBG_G(exec)); PHPDBG_G(exec) = NULL; @@ -448,7 +450,7 @@ static void php_sapi_phpdbg_log_message(char *message TSRMLS_DC) /* {{{ */ } do { - switch (phpdbg_interactive(TSRMLS_C)) { + switch (phpdbg_interactive(1 TSRMLS_CC)) { case PHPDBG_LEAVE: case PHPDBG_FINISH: case PHPDBG_UNTIL: @@ -817,7 +819,7 @@ void phpdbg_sigio_handler(int sig, siginfo_t *info, void *context) /* {{{ */ { int flags; size_t newlen; - size_t i; + size_t i/*, last_nl*/; TSRMLS_FETCH(); // if (!(info->si_band & POLLIN)) { @@ -853,7 +855,11 @@ void phpdbg_sigio_handler(int sig, siginfo_t *info, void *context) /* {{{ */ PHPDBG_G(flags) |= PHPDBG_IS_SIGNALED; } break; - } +/* case '\n': + zend_llist_add_element(PHPDBG_G(stdin), strndup() + last_nl = PHPDBG_G(stdin_buf).len + i; + break; +*/ } } off += i; } while (0); @@ -870,6 +876,9 @@ void phpdbg_signal_handler(int sig, siginfo_t *info, void *context) /* {{{ */ switch (sig) { case SIGBUS: case SIGSEGV: + if (PHPDBG_G(sigsegv_bailout)) { + LONGJMP(*PHPDBG_G(sigsegv_bailout), FAILURE); + } is_handled = phpdbg_watchpoint_segfault_handler(info, context TSRMLS_CC); if (is_handled == FAILURE) { #ifdef ZEND_SIGNALS @@ -1255,6 +1264,8 @@ phpdbg_main: zend_activate(TSRMLS_C); + phpdbg_init_list(TSRMLS_C); + PHPDBG_G(original_free_function) = mm_heap->_free; mm_heap->_free = phpdbg_watch_efree; @@ -1291,6 +1302,8 @@ phpdbg_main: php_output_activate(TSRMLS_C); php_output_deactivate(TSRMLS_C); + php_output_activate(TSRMLS_C); + /* do not install sigint handlers for remote consoles */ /* sending SIGINT then provides a decent way of shutting down the server */ #ifndef _WIN32 @@ -1397,7 +1410,7 @@ phpdbg_interact: /* phpdbg main() */ do { zend_try { - phpdbg_interactive(TSRMLS_C); + phpdbg_interactive(1 TSRMLS_CC); } zend_catch { if ((PHPDBG_G(flags) & PHPDBG_IS_CLEANING)) { FILE *bp_tmp_fp = fopen(bp_tmp_file, "w"); diff --git a/phpdbg.h b/phpdbg.h index 105614c869..941b539d11 100644 --- a/phpdbg.h +++ b/phpdbg.h @@ -2,7 +2,7 @@ +----------------------------------------------------------------------+ | PHP Version 5 | +----------------------------------------------------------------------+ - | Copyright (c) 1997-2014 The PHP Group | + | Copyright (c) 7-4 The PHP Group | +----------------------------------------------------------------------+ | This source file is subject to version 3.01 of the PHP license, | | that is bundled with this package in the file LICENSE, and is | @@ -149,8 +149,10 @@ int phpdbg_do_parse(phpdbg_param_t *stack, char *input TSRMLS_DC); #define PHPDBG_SHOW_REFCOUNTS (1<<29) +#define PHPDBG_IN_SIGNAL_HANDLER (1<<30) + #define PHPDBG_SEEK_MASK (PHPDBG_IN_UNTIL|PHPDBG_IN_FINISH|PHPDBG_IN_LEAVE) -#define PHPDBG_BP_RESOLVE_MASK (PHPDBG_HAS_FUNCTION_OPLINE_BP|PHPDBG_HAS_METHOD_OPLINE_BP|PHPDBG_HAS_FILE_OPLINE_BP) +#define PHPDBG_BP_RESOLVE_MASK (PHPDBG_HAS_FUNCTION_OPLINE_BP|PHPDBG_HAS_METHOD_OPLINE_BP|PHPDBG_HAS_FILE_OPLINE_BP) #define PHPDBG_BP_MASK (PHPDBG_HAS_FILE_BP|PHPDBG_HAS_SYM_BP|PHPDBG_HAS_METHOD_BP|PHPDBG_HAS_OPLINE_BP|PHPDBG_HAS_COND_BP|PHPDBG_HAS_OPCODE_BP|PHPDBG_HAS_FUNCTION_OPLINE_BP|PHPDBG_HAS_METHOD_OPLINE_BP|PHPDBG_HAS_FILE_OPLINE_BP) #ifndef _WIN32 @@ -174,6 +176,21 @@ int phpdbg_do_parse(phpdbg_param_t *stack, char *input TSRMLS_DC); #define PHPDBG_STDERR 2 #define PHPDBG_IO_FDS 3 /* }}} */ +#define phpdbg_try_access \ + { \ + JMP_BUF *__orig_bailout = PHPDBG_G(sigsegv_bailout); \ + JMP_BUF __bailout; \ + \ + PHPDBG_G(sigsegv_bailout) = &__bailout; \ + if (SETJMP(__bailout) == 0) { +#define phpdbg_catch_access \ + } else { \ + PHPDBG_G(sigsegv_bailout) = __orig_bailout; +#define phpdbg_end_try_access() \ + } \ + PHPDBG_G(sigsegv_bailout) = __orig_bailout; \ + } + /* {{{ structs */ ZEND_BEGIN_MODULE_GLOBALS(phpdbg) @@ -203,6 +220,9 @@ ZEND_BEGIN_MODULE_GLOBALS(phpdbg) int bp_count; /* breakpoint count */ int vmret; /* return from last opcode handler execution */ + zend_op_array *(*compile_file)(zend_file_handle *file_handle, int type TSRMLS_DC); + HashTable file_sources; + FILE *oplog; /* opline log */ struct { FILE *ptr; @@ -218,6 +238,8 @@ ZEND_BEGIN_MODULE_GLOBALS(phpdbg) int input_buflen; /* length of stdin input buffer */ phpdbg_signal_safe_mem sigsafe_mem; /* memory to use in async safe environment (only once!) */ + JMP_BUF *sigsegv_bailout; /* bailout address for accesibility probing */ + zend_ulong flags; /* phpdbg flags */ ZEND_END_MODULE_GLOBALS(phpdbg) /* }}} */ diff --git a/phpdbg_break.c b/phpdbg_break.c index be76b22b05..386d4d9562 100644 --- a/phpdbg_break.c +++ b/phpdbg_break.c @@ -28,15 +28,15 @@ ZEND_EXTERN_MODULE_GLOBALS(phpdbg); -#define PHPDBG_BREAK_COMMAND_D(f, h, a, m, l, s) \ - PHPDBG_COMMAND_D_EXP(f, h, a, m, l, s, &phpdbg_prompt_commands[10]) +#define PHPDBG_BREAK_COMMAND_D(f, h, a, m, l, s, flags) \ + PHPDBG_COMMAND_D_EXP(f, h, a, m, l, s, &phpdbg_prompt_commands[10], flags) /** * Commands */ const phpdbg_command_t phpdbg_break_commands[] = { - PHPDBG_BREAK_COMMAND_D(at, "specify breakpoint by location and condition", '@', break_at, NULL, "*c"), - PHPDBG_BREAK_COMMAND_D(del, "delete breakpoint by identifier number", '~', break_del, NULL, "n"), + PHPDBG_BREAK_COMMAND_D(at, "specify breakpoint by location and condition", '@', break_at, NULL, "*c", 0), + PHPDBG_BREAK_COMMAND_D(del, "delete breakpoint by identifier number", '~', break_del, NULL, "n", 0), PHPDBG_END_COMMAND }; diff --git a/phpdbg_cmd.c b/phpdbg_cmd.c index 0ea6c12127..e9811dc43b 100644 --- a/phpdbg_cmd.c +++ b/phpdbg_cmd.c @@ -644,9 +644,9 @@ PHPDBG_API const phpdbg_command_t* phpdbg_stack_resolve(const phpdbg_command_t * phpdbg_param_t *name = *top; const phpdbg_command_t *matched[3] = {NULL, NULL, NULL}; ulong matches = 0L; - + while (command && command->name && command->handler) { - if ((name->len == 1) || (command->name_len >= name->len)) { + if (name->len == 1 || command->name_len >= name->len) { /* match single letter alias */ if (command->alias && (name->len == 1)) { if (command->alias == (*name->str)) { @@ -654,81 +654,72 @@ PHPDBG_API const phpdbg_command_t* phpdbg_stack_resolve(const phpdbg_command_t * matches++; } } else { - /* match full, case insensitive, command name */ if (strncasecmp(command->name, name->str, name->len) == SUCCESS) { if (matches < 3) { - /* only allow abbreviating commands that can be aliased */ if (((name->len != command->name_len) && command->alias) || (name->len == command->name_len)) { matched[matches] = command; matches++; } - - + /* exact match */ - if (name->len == command->name_len) + if (name->len == command->name_len) { break; - } else break; + } + } else { + break; + } } } } - + command++; } - + switch (matches) { - case 0: { + case 0: if (parent) { - asprintf( - why, - "The command \"%s %s\" could not be found", - parent->name, name->str); - } else asprintf( - why, - "The command \"%s\" could not be found", - name->str); - } return parent; - - case 1: { + spprintf(why, 0, "The command \"%s %s\" could not be found", parent->name, name->str); + } else { + spprintf(why, 0, "The command \"%s\" could not be found", name->str); + } + return parent; + + case 1: (*top) = (*top)->next; command = matched[0]; - } break; - + break; + default: { char *list = NULL; zend_uint it = 0; size_t pos = 0; - + while (it < matches) { if (!list) { - list = malloc( - matched[it]->name_len + 1 + - ((it+1) < matches ? sizeof(", ")-1 : 0)); + list = emalloc(matched[it]->name_len + 1 + (it + 1 < matches ? sizeof(", ") - 1 : 0)); } else { - list = realloc(list, - (pos + matched[it]->name_len) + 1 + - ((it+1) < matches ? sizeof(", ")-1 : 0)); + list = erealloc(list, (pos + matched[it]->name_len) + 1 + (it + 1 < matches ? sizeof(", ") - 1 : 0)); } memcpy(&list[pos], matched[it]->name, matched[it]->name_len); pos += matched[it]->name_len; - if ((it+1) < matches) { - memcpy(&list[pos], ", ", sizeof(", ")-1); + if ((it + 1) < matches) { + memcpy(&list[pos], ", ", sizeof(", ") - 1); pos += (sizeof(", ") - 1); } - + list[pos] = 0; it++; } - - asprintf( - why, - "The command \"%s\" is ambigious, matching %lu commands (%s)", - name->str, matches, list); - free(list); - } return NULL; + + spprintf(why, 0, "The command \"%s\" is ambigious, matching %lu commands (%s)", name->str, matches, list); + efree(list); + + return NULL; + } } if (command->subs && (*top) && ((*top)->type == STR_PARAM)) { @@ -741,51 +732,64 @@ PHPDBG_API const phpdbg_command_t* phpdbg_stack_resolve(const phpdbg_command_t * } /* }}} */ /* {{{ */ -PHPDBG_API int phpdbg_stack_execute(phpdbg_param_t *stack, char **why TSRMLS_DC) { +PHPDBG_API int phpdbg_stack_execute(phpdbg_param_t *stack, char **why, zend_bool allow_async_unsafe TSRMLS_DC) { phpdbg_param_t *top = NULL; const phpdbg_command_t *handler = NULL; - + if (stack->type != STACK_PARAM) { - asprintf( - why, "The passed argument was not a stack !!"); + spprintf(why, 0, "The passed argument was not a stack !!"); return FAILURE; } - + if (!stack->len) { - asprintf( - why, "The stack contains nothing !!"); + spprintf(why, 0, "The stack contains nothing !!"); return FAILURE; } - + top = (phpdbg_param_t*) stack->next; - + switch (top->type) { case EVAL_PARAM: - return PHPDBG_COMMAND_HANDLER(ev)(top TSRMLS_CC); + if (allow_async_unsafe) { + return PHPDBG_COMMAND_HANDLER(ev)(top TSRMLS_CC); + } + spprintf(why, 0, "ev command is disallowed during hard interrupt"); + return FAILURE; case RUN_PARAM: - return PHPDBG_COMMAND_HANDLER(run)(top TSRMLS_CC); - + if (allow_async_unsafe) { + return PHPDBG_COMMAND_HANDLER(run)(top TSRMLS_CC); + } + spprintf(why, 0, "run command is disallowed during hard interrupt"); + return FAILURE; + case SHELL_PARAM: - return PHPDBG_COMMAND_HANDLER(sh)(top TSRMLS_CC); - + if (allow_async_unsafe) { + return PHPDBG_COMMAND_HANDLER(sh)(top TSRMLS_CC); + } + spprintf(why, 0, "sh command is disallowed during hard interrupt"); + return FAILURE; + case STR_PARAM: { - handler = phpdbg_stack_resolve( - phpdbg_prompt_commands, NULL, &top, why); - + handler = phpdbg_stack_resolve(phpdbg_prompt_commands, NULL, &top, why); + if (handler) { + if (!allow_async_unsafe && !(handler->flags & PHPDBG_ASYNC_SAFE)) { + spprintf(why, 0, "%s command is disallowed during hard interrupt", handler->name); + return FAILURE; + } + if (phpdbg_stack_verify(handler, &top, why TSRMLS_CC) == SUCCESS) { return handler->handler(top TSRMLS_CC); } } } return FAILURE; - + default: - asprintf( - why, "The first parameter makes no sense !!"); + spprintf(why, 0, "The first parameter makes no sense !!"); return FAILURE; } - + return SUCCESS; } /* }}} */ @@ -909,4 +913,3 @@ PHPDBG_API void phpdbg_destroy_input(char **input TSRMLS_DC) /*{{{ */ { efree(*input); } /* }}} */ - diff --git a/phpdbg_cmd.h b/phpdbg_cmd.h index 571d065f59..45f1579186 100644 --- a/phpdbg_cmd.h +++ b/phpdbg_cmd.h @@ -86,6 +86,8 @@ struct _phpdbg_param { #define YYSTYPE phpdbg_param_t #endif +#define PHPDBG_ASYNC_SAFE 1 + typedef int (*phpdbg_command_handler_t)(const phpdbg_param_t* TSRMLS_DC); typedef struct _phpdbg_command_t phpdbg_command_t; @@ -97,8 +99,9 @@ struct _phpdbg_command_t { char alias; /* Alias */ phpdbg_command_handler_t handler; /* Command handler */ const phpdbg_command_t *subs; /* Sub Commands */ - char *args; /* Argument Spec */ - const phpdbg_command_t *parent; /* Parent Command */ + char *args; /* Argument Spec */ + const phpdbg_command_t *parent; /* Parent Command */ + zend_bool flags; /* General flags */ }; /* }}} */ @@ -135,7 +138,7 @@ PHPDBG_API void phpdbg_destroy_input(char** TSRMLS_DC); PHPDBG_API void phpdbg_stack_push(phpdbg_param_t *stack, phpdbg_param_t *param); PHPDBG_API const phpdbg_command_t* phpdbg_stack_resolve(const phpdbg_command_t *commands, const phpdbg_command_t *parent, phpdbg_param_t **top, char **why); PHPDBG_API int phpdbg_stack_verify(const phpdbg_command_t *command, phpdbg_param_t **stack, char **why TSRMLS_DC); -PHPDBG_API int phpdbg_stack_execute(phpdbg_param_t *stack, char **why TSRMLS_DC); +PHPDBG_API int phpdbg_stack_execute(phpdbg_param_t *stack, char **why, zend_bool allow_async_unsafe TSRMLS_DC); PHPDBG_API void phpdbg_stack_free(phpdbg_param_t *stack); /* @@ -155,20 +158,20 @@ PHPDBG_API void phpdbg_param_debug(const phpdbg_param_t *param, const char *msg) */ #define PHPDBG_COMMAND_HANDLER(name) phpdbg_do_##name -#define PHPDBG_COMMAND_D_EXP(name, tip, alias, handler, children, args, parent) \ - {PHPDBG_STRL(#name), tip, sizeof(tip)-1, alias, phpdbg_do_##handler, children, args, parent} +#define PHPDBG_COMMAND_D_EXP(name, tip, alias, handler, children, args, parent, flags) \ + {PHPDBG_STRL(#name), tip, sizeof(tip)-1, alias, phpdbg_do_##handler, children, args, parent, flags} -#define PHPDBG_COMMAND_D_EX(name, tip, alias, handler, children, args) \ - {PHPDBG_STRL(#name), tip, sizeof(tip)-1, alias, phpdbg_do_##handler, children, args, NULL} +#define PHPDBG_COMMAND_D_EX(name, tip, alias, handler, children, args, flags) \ + {PHPDBG_STRL(#name), tip, sizeof(tip)-1, alias, phpdbg_do_##handler, children, args, NULL, flags} -#define PHPDBG_COMMAND_D(name, tip, alias, children, args) \ - {PHPDBG_STRL(#name), tip, sizeof(tip)-1, alias, phpdbg_do_##name, children, args, NULL} +#define PHPDBG_COMMAND_D(name, tip, alias, children, args, flags) \ + {PHPDBG_STRL(#name), tip, sizeof(tip)-1, alias, phpdbg_do_##name, children, args, NULL, flags} #define PHPDBG_COMMAND(name) int phpdbg_do_##name(const phpdbg_param_t *param TSRMLS_DC) #define PHPDBG_COMMAND_ARGS param TSRMLS_CC -#define PHPDBG_END_COMMAND {NULL, 0, NULL, 0, '\0', NULL, NULL, '\0', NULL} +#define PHPDBG_END_COMMAND {NULL, 0, NULL, 0, '\0', NULL, NULL, '\0', NULL, 0} /* * Default Switch Case diff --git a/phpdbg_frame.c b/phpdbg_frame.c index a235fe8cb0..62b3a66feb 100644 --- a/phpdbg_frame.c +++ b/phpdbg_frame.c @@ -56,15 +56,20 @@ void phpdbg_switch_frame(int frame TSRMLS_DC) /* {{{ */ return; } - while (execute_data) { - if (i++ == frame) { - break; - } + phpdbg_try_access { + while (execute_data) { + if (i++ == frame) { + break; + } - do { - execute_data = execute_data->prev_execute_data; - } while (execute_data && execute_data->opline == NULL); - } + do { + execute_data = execute_data->prev_execute_data; + } while (execute_data && execute_data->opline == NULL); + } + } phpdbg_catch_access { + phpdbg_error("Couldn't switch frames, invalid data source"); + return; + } phpdbg_end_try_access(); if (execute_data == NULL) { phpdbg_error("No frame #%d", frame); @@ -105,16 +110,12 @@ static void phpdbg_dump_prototype(zval **tmp TSRMLS_DC) /* {{{ */ zval **funcname, **class, **type, **args, **argstmp; char is_class; - zend_hash_find(Z_ARRVAL_PP(tmp), "function", sizeof("function"), - (void **)&funcname); + zend_hash_find(Z_ARRVAL_PP(tmp), "function", sizeof("function"), (void **) &funcname); - if ((is_class = zend_hash_find(Z_ARRVAL_PP(tmp), - "object", sizeof("object"), (void **)&class)) == FAILURE) { - is_class = zend_hash_find(Z_ARRVAL_PP(tmp), "class", sizeof("class"), - (void **)&class); + if ((is_class = zend_hash_find(Z_ARRVAL_PP(tmp), "object", sizeof("object"), (void **) &class)) == FAILURE) { + is_class = zend_hash_find(Z_ARRVAL_PP(tmp), "class", sizeof("class"), (void **)&class); } else { - zend_get_object_classname(*class, (const char **)&Z_STRVAL_PP(class), - (zend_uint *)&Z_STRLEN_PP(class) TSRMLS_CC); + zend_get_object_classname(*class, (const char **) &Z_STRVAL_PP(class), (zend_uint *) &Z_STRLEN_PP(class) TSRMLS_CC); } if (is_class == SUCCESS) { @@ -127,18 +128,22 @@ static void phpdbg_dump_prototype(zval **tmp TSRMLS_DC) /* {{{ */ Z_STRVAL_PP(funcname) ); - if (zend_hash_find(Z_ARRVAL_PP(tmp), "args", sizeof("args"), - (void **)&args) == SUCCESS) { + if (zend_hash_find(Z_ARRVAL_PP(tmp), "args", sizeof("args"), (void **) &args) == SUCCESS) { HashPosition iterator; - const zend_function *func = phpdbg_get_function( - Z_STRVAL_PP(funcname), is_class == FAILURE ? NULL : Z_STRVAL_PP(class) TSRMLS_CC); - const zend_arg_info *arginfo = func ? func->common.arg_info : NULL; + const zend_function *func; + const zend_arg_info *arginfo = NULL; int j = 0, m = func ? func->common.num_args : 0; zend_bool is_variadic = 0; + phpdbg_try_access { + /* assuming no autoloader call is necessary, class should have been loaded if it's in backtrace ... */ + if ((func = phpdbg_get_function(Z_STRVAL_PP(funcname), is_class == FAILURE ? NULL : Z_STRVAL_PP(class) TSRMLS_CC))) { + arginfo = func->common.arg_info; + } + } phpdbg_end_try_access(); + zend_hash_internal_pointer_reset_ex(Z_ARRVAL_PP(args), &iterator); - while (zend_hash_get_current_data_ex(Z_ARRVAL_PP(args), - (void **) &argstmp, &iterator) == SUCCESS) { + while (zend_hash_get_current_data_ex(Z_ARRVAL_PP(args), (void **) &argstmp, &iterator) == SUCCESS) { if (j) { phpdbg_write(", "); } @@ -174,8 +179,15 @@ void phpdbg_dump_backtrace(size_t num TSRMLS_DC) /* {{{ */ phpdbg_error("Invalid backtrace size %d", limit); } - zend_fetch_debug_backtrace( - &zbacktrace, 0, 0, limit TSRMLS_CC); + phpdbg_try_access { + zend_fetch_debug_backtrace(&zbacktrace, 0, 0, limit TSRMLS_CC); + } phpdbg_catch_access { + phpdbg_try_access { + zval_dtor(&zbacktrace); + } phpdbg_end_try_access(); + phpdbg_error("Couldn't fetch backtrace, invalid data source"); + return; + } phpdbg_end_try_access(); zend_hash_internal_pointer_reset_ex(Z_ARRVAL(zbacktrace), &position); zend_hash_get_current_data_ex(Z_ARRVAL(zbacktrace), (void**)&tmp, &position); @@ -184,8 +196,7 @@ void phpdbg_dump_backtrace(size_t num TSRMLS_DC) /* {{{ */ zend_hash_find(Z_ARRVAL_PP(tmp), "line", sizeof("line"), (void **)&line); zend_hash_move_forward_ex(Z_ARRVAL(zbacktrace), &position); - if (zend_hash_get_current_data_ex(Z_ARRVAL(zbacktrace), - (void**)&tmp, &position) == FAILURE) { + if (zend_hash_get_current_data_ex(Z_ARRVAL(zbacktrace), (void**)&tmp, &position) == FAILURE) { phpdbg_write("frame #%d: {main} at %s:%ld", i, Z_STRVAL_PP(file), Z_LVAL_PP(line)); break; } diff --git a/phpdbg_info.c b/phpdbg_info.c index a783bf44fc..8d4bc530db 100644 --- a/phpdbg_info.c +++ b/phpdbg_info.c @@ -27,19 +27,19 @@ ZEND_EXTERN_MODULE_GLOBALS(phpdbg); -#define PHPDBG_INFO_COMMAND_D(f, h, a, m, l, s) \ - PHPDBG_COMMAND_D_EXP(f, h, a, m, l, s, &phpdbg_prompt_commands[14]) +#define PHPDBG_INFO_COMMAND_D(f, h, a, m, l, s, flags) \ + PHPDBG_COMMAND_D_EXP(f, h, a, m, l, s, &phpdbg_prompt_commands[14], flags) const phpdbg_command_t phpdbg_info_commands[] = { - PHPDBG_INFO_COMMAND_D(break, "show breakpoints", 'b', info_break, NULL, 0), - PHPDBG_INFO_COMMAND_D(files, "show included files", 'F', info_files, NULL, 0), - PHPDBG_INFO_COMMAND_D(classes, "show loaded classes", 'c', info_classes, NULL, 0), - PHPDBG_INFO_COMMAND_D(funcs, "show loaded classes", 'f', info_funcs, NULL, 0), - PHPDBG_INFO_COMMAND_D(error, "show last error", 'e', info_error, NULL, 0), - PHPDBG_INFO_COMMAND_D(vars, "show active variables", 'v', info_vars, NULL, 0), - PHPDBG_INFO_COMMAND_D(globals, "show superglobals", 'g', info_globals, NULL, 0), - PHPDBG_INFO_COMMAND_D(literal, "show active literal constants", 'l', info_literal, NULL, 0), - PHPDBG_INFO_COMMAND_D(memory, "show memory manager stats", 'm', info_memory, NULL, 0), + PHPDBG_INFO_COMMAND_D(break, "show breakpoints", 'b', info_break, NULL, 0, PHPDBG_ASYNC_SAFE), + PHPDBG_INFO_COMMAND_D(files, "show included files", 'F', info_files, NULL, 0, PHPDBG_ASYNC_SAFE), + PHPDBG_INFO_COMMAND_D(classes, "show loaded classes", 'c', info_classes, NULL, 0, PHPDBG_ASYNC_SAFE), + PHPDBG_INFO_COMMAND_D(funcs, "show loaded classes", 'f', info_funcs, NULL, 0, PHPDBG_ASYNC_SAFE), + PHPDBG_INFO_COMMAND_D(error, "show last error", 'e', info_error, NULL, 0, PHPDBG_ASYNC_SAFE), + PHPDBG_INFO_COMMAND_D(vars, "show active variables", 'v', info_vars, NULL, 0, PHPDBG_ASYNC_SAFE), + PHPDBG_INFO_COMMAND_D(globals, "show superglobals", 'g', info_globals, NULL, 0, PHPDBG_ASYNC_SAFE), + PHPDBG_INFO_COMMAND_D(literal, "show active literal constants", 'l', info_literal, NULL, 0, PHPDBG_ASYNC_SAFE), + PHPDBG_INFO_COMMAND_D(memory, "show memory manager stats", 'm', info_memory, NULL, 0, PHPDBG_ASYNC_SAFE), PHPDBG_END_COMMAND }; @@ -63,15 +63,21 @@ PHPDBG_INFO(files) /* {{{ */ HashPosition pos; char *fname; - phpdbg_notice("Included files: %d", - zend_hash_num_elements(&EG(included_files))); - - zend_hash_internal_pointer_reset_ex(&EG(included_files), &pos); - while (zend_hash_get_current_key_ex(&EG(included_files), &fname, - NULL, NULL, 0, &pos) == HASH_KEY_IS_STRING) { - phpdbg_writeln("File: %s", fname); - zend_hash_move_forward_ex(&EG(included_files), &pos); - } + phpdbg_try_access { + phpdbg_notice("Included files: %d", zend_hash_num_elements(&EG(included_files))); + } phpdbg_catch_access { + phpdbg_error("Could not fetch included file count, invalid data source"); + } phpdbg_end_try_access(); + + phpdbg_try_access { + zend_hash_internal_pointer_reset_ex(&EG(included_files), &pos); + while (zend_hash_get_current_key_ex(&EG(included_files), &fname, NULL, NULL, 0, &pos) == HASH_KEY_IS_STRING) { + phpdbg_writeln("File: %s", fname); + zend_hash_move_forward_ex(&EG(included_files), &pos); + } + } phpdbg_catch_access { + phpdbg_error("Could not fetch file name, invalid data source, aborting included file listing"); + } phpdbg_end_try_access(); return SUCCESS; } /* }}} */ @@ -79,8 +85,11 @@ PHPDBG_INFO(files) /* {{{ */ PHPDBG_INFO(error) /* {{{ */ { if (PG(last_error_message)) { - phpdbg_writeln("Last error: %s at %s line %d", - PG(last_error_message), PG(last_error_file), PG(last_error_lineno)); + phpdbg_try_access { + phpdbg_writeln("Last error: %s at %s line %d", PG(last_error_message), PG(last_error_file), PG(last_error_lineno)); + } phpdbg_catch_access { + phpdbg_notice("No error found!"); + } phpdbg_end_try_access(); } else { phpdbg_notice("No error found!"); } @@ -89,7 +98,11 @@ PHPDBG_INFO(error) /* {{{ */ static int phpdbg_arm_auto_global(zend_auto_global *auto_global TSRMLS_DC) { if (auto_global->armed) { - auto_global->armed = auto_global->auto_global_callback(auto_global->name, auto_global->name_len TSRMLS_CC); + if (PHPDBG_G(flags) & PHPDBG_IN_SIGNAL_HANDLER) { + phpdbg_notice("Cannot show information about superglobal variable %.*s", auto_global->name_len, auto_global->name); + } else { + auto_global->armed = auto_global->auto_global_callback(auto_global->name, auto_global->name_len TSRMLS_CC); + } } return 0; @@ -117,6 +130,7 @@ static int phpdbg_print_symbols(zend_bool show_globals TSRMLS_DC) { if (show_globals) { + /* that array should only be manipulated during init, so safe for async access during execution */ zend_hash_apply(CG(auto_globals), (apply_func_t) phpdbg_arm_auto_global TSRMLS_CC); symtable = &EG(symbol_table); } else { @@ -125,15 +139,18 @@ static int phpdbg_print_symbols(zend_bool show_globals TSRMLS_DC) { zend_hash_init(&vars, 8, NULL, NULL, 0); - zend_hash_internal_pointer_reset_ex(symtable, &pos); - while (zend_hash_get_current_key_ex(symtable, &var, - NULL, NULL, 0, &pos) == HASH_KEY_IS_STRING) { - zend_hash_get_current_data_ex(symtable, (void **)&data, &pos); - if (zend_is_auto_global(var, strlen(var) TSRMLS_CC) ^ !show_globals) { - zend_hash_update(&vars, var, strlen(var)+1, (void**)data, sizeof(zval*), NULL); + phpdbg_try_access { + zend_hash_internal_pointer_reset_ex(symtable, &pos); + while (zend_hash_get_current_key_ex(symtable, &var, NULL, NULL, 0, &pos) == HASH_KEY_IS_STRING) { + zend_hash_get_current_data_ex(symtable, (void **)&data, &pos); + if (zend_is_auto_global(var, strlen(var) TSRMLS_CC) ^ !show_globals) { + zend_hash_update(&vars, var, strlen(var)+1, (void**)data, sizeof(zval*), NULL); + } + zend_hash_move_forward_ex(symtable, &pos); } - zend_hash_move_forward_ex(symtable, &pos); - } + } phpdbg_catch_access { + phpdbg_error("Cannot fetch all data from the symbol table, invalid data source"); + } phpdbg_end_try_access(); if (show_globals) { phpdbg_notice("Superglobal variables (%d)", zend_hash_num_elements(&vars)); @@ -165,54 +182,51 @@ static int phpdbg_print_symbols(zend_bool show_globals TSRMLS_DC) { zend_hash_get_current_data_ex(&vars, (void**) &data, &pos) == SUCCESS; zend_hash_move_forward_ex(&vars, &pos)) { char *var; + zend_bool invalid_data = 1; zend_hash_get_current_key_ex(&vars, &var, NULL, NULL, 0, &pos); - if (*data) { - phpdbg_write( - "%p\t%d\t", - *data, - Z_REFCOUNT_PP(data)); - - switch (Z_TYPE_PP(data)) { - case IS_STRING: phpdbg_write("(string)\t"); break; - case IS_LONG: phpdbg_write("(integer)\t"); break; - case IS_DOUBLE: phpdbg_write("(float)\t"); break; - case IS_RESOURCE: phpdbg_write("(resource)\t"); break; - case IS_ARRAY: phpdbg_write("(array)\t"); break; - case IS_OBJECT: phpdbg_write("(object)\t"); break; - case IS_NULL: phpdbg_write("(null)\t"); break; - } + phpdbg_try_access { + if (!(invalid_data = !*data)) { + phpdbg_write("%p\t%d\t", *data, Z_REFCOUNT_PP(data)); + + switch (Z_TYPE_PP(data)) { + case IS_STRING: phpdbg_write("(string)\t"); break; + case IS_LONG: phpdbg_write("(integer)\t"); break; + case IS_DOUBLE: phpdbg_write("(float)\t"); break; + case IS_RESOURCE: phpdbg_write("(resource)\t"); break; + case IS_ARRAY: phpdbg_write("(array)\t"); break; + case IS_OBJECT: phpdbg_write("(object)\t"); break; + case IS_NULL: phpdbg_write("(null)\t"); break; + } - if (Z_TYPE_PP(data) == IS_RESOURCE) { - int type; - - phpdbg_writeln( - "%s$%s", Z_ISREF_PP(data) ? "&": "", var); - if (zend_list_find(Z_RESVAL_PP(data), &type)) { - phpdbg_write( - "|-------(typeof)------> (%s)", - zend_rsrc_list_get_rsrc_type(type TSRMLS_CC)); - } else { - phpdbg_write( - "|-------(typeof)------> (unknown)"); + phpdbg_writeln("%s$%s", Z_ISREF_PP(data) ? "&": "", var); + + if (Z_TYPE_PP(data) == IS_RESOURCE) { + int type; + + phpdbg_try_access { + if (zend_list_find(Z_RESVAL_PP(data), &type)) { + phpdbg_writeln("|-------(typeof)------> (%s)", zend_rsrc_list_get_rsrc_type(type TSRMLS_CC)); + } else { + phpdbg_writeln("|-------(typeof)------> (unknown)"); + } + } phpdbg_catch_access { + phpdbg_writeln("|-------(typeof)------> (unknown)"); + } phpdbg_end_try_access(); + } else if (Z_TYPE_PP(data) == IS_OBJECT) { + phpdbg_try_access { + phpdbg_writeln("|-----(instanceof)----> (%s)", Z_OBJCE_PP(data)->name); + } phpdbg_catch_access { + phpdbg_writeln("|-----(instanceof)----> (unknown)"); + } phpdbg_end_try_access(); } - phpdbg_writeln(EMPTY); - } else if (Z_TYPE_PP(data) == IS_OBJECT) { - phpdbg_writeln( - "%s$%s", Z_ISREF_PP(data) ? "&": "", var); - phpdbg_write( - "|-----(instanceof)----> (%s)", Z_OBJCE_PP(data)->name); - phpdbg_writeln(EMPTY); - } else { - phpdbg_write( - "%s$%s", Z_ISREF_PP(data) ? "&": "", var); } - } else { - phpdbg_write( - "n/a\tn/a\tn/a\t$%s", var); + } phpdbg_end_try_access(); + + if (invalid_data) { + phpdbg_writeln("n/a\tn/a\tn/a\t$%s", var); } - phpdbg_writeln(EMPTY); } } @@ -233,6 +247,7 @@ PHPDBG_INFO(globals) /* {{{ */ PHPDBG_INFO(literal) /* {{{ */ { + /* literals are assumed to not be manipulated during executing of their op_array and as such async safe */ if ((EG(in_execution) && EG(active_op_array)) || PHPDBG_G(ops)) { zend_op_array *ops = EG(active_op_array) ? EG(active_op_array) : PHPDBG_G(ops); int literal = 0, count = ops->last_literal-1; @@ -258,8 +273,7 @@ PHPDBG_INFO(literal) /* {{{ */ while (literal < ops->last_literal) { if (Z_TYPE(ops->literals[literal].constant) != IS_NULL) { phpdbg_write("|-------- C%u -------> [", literal); - zend_print_zval( - &ops->literals[literal].constant, 0); + zend_print_zval(&ops->literals[literal].constant, 0); phpdbg_write("]"); phpdbg_writeln(EMPTY); } @@ -274,18 +288,31 @@ PHPDBG_INFO(literal) /* {{{ */ PHPDBG_INFO(memory) /* {{{ */ { - if (is_zend_mm(TSRMLS_C)) { + size_t used, real, peak_used, peak_real; + zend_mm_heap *heap; + zend_bool is_mm; + + if (PHPDBG_G(flags) & PHPDBG_IN_SIGNAL_HANDLER) { + heap = zend_mm_set_heap(phpdbg_original_heap_sigsafe_mem(TSRMLS_C) TSRMLS_CC); + } + if ((is_mm = is_zend_mm(TSRMLS_C))) { + used = zend_memory_usage(0 TSRMLS_CC); + real = zend_memory_usage(1 TSRMLS_CC); + peak_used = zend_memory_peak_usage(0 TSRMLS_CC); + peak_real = zend_memory_peak_usage(1 TSRMLS_CC); + } + if (PHPDBG_G(flags) & PHPDBG_IN_SIGNAL_HANDLER) { + zend_mm_set_heap(heap TSRMLS_CC); + } + + if (is_mm) { phpdbg_notice("Memory Manager Information"); phpdbg_notice("Current"); - phpdbg_writeln("|-------> Used:\t%.3f kB", - (float) (zend_memory_usage(0 TSRMLS_CC)/1024)); - phpdbg_writeln("|-------> Real:\t%.3f kB", - (float) (zend_memory_usage(1 TSRMLS_CC)/1024)); + phpdbg_writeln("|-------> Used:\t%.3f kB", (float) (used / 1024)); + phpdbg_writeln("|-------> Real:\t%.3f kB", (float) (real / 1024)); phpdbg_notice("Peak"); - phpdbg_writeln("|-------> Used:\t%.3f kB", - (float) (zend_memory_peak_usage(0 TSRMLS_CC)/1024)); - phpdbg_writeln("|-------> Real:\t%.3f kB", - (float) (zend_memory_peak_usage(1 TSRMLS_CC)/1024)); + phpdbg_writeln("|-------> Used:\t%.3f kB", (float) (peak_used / 1024)); + phpdbg_writeln("|-------> Real:\t%.3f kB", (float) (peak_real / 1024)); } else { phpdbg_error("Memory Manager Disabled!"); } @@ -314,19 +341,21 @@ PHPDBG_INFO(classes) /* {{{ */ zend_hash_init(&classes, 8, NULL, NULL, 0); - for (zend_hash_internal_pointer_reset_ex(EG(class_table), &position); - zend_hash_get_current_data_ex(EG(class_table), (void**)&ce, &position) == SUCCESS; - zend_hash_move_forward_ex(EG(class_table), &position)) { - - if ((*ce)->type == ZEND_USER_CLASS) { - zend_hash_next_index_insert( - &classes, ce, sizeof(ce), NULL); + phpdbg_try_access { + for (zend_hash_internal_pointer_reset_ex(EG(class_table), &position); + zend_hash_get_current_data_ex(EG(class_table), (void**)&ce, &position) == SUCCESS; + zend_hash_move_forward_ex(EG(class_table), &position)) { + if ((*ce)->type == ZEND_USER_CLASS) { + zend_hash_next_index_insert(&classes, ce, sizeof(ce), NULL); + } } - } + } phpdbg_catch_access { + phpdbg_notice("Not all classes could be fetched, possibly invalid data source"); + } phpdbg_end_try_access(); - phpdbg_notice("User Classes (%d)", - zend_hash_num_elements(&classes)); + phpdbg_notice("User Classes (%d)", zend_hash_num_elements(&classes)); + /* once added, assume that classes are stable... until shutdown. */ for (zend_hash_internal_pointer_reset_ex(&classes, &position); zend_hash_get_current_data_ex(&classes, (void**)&ce, &position) == SUCCESS; zend_hash_move_forward_ex(&classes, &position)) { @@ -367,15 +396,17 @@ PHPDBG_INFO(funcs) /* {{{ */ zend_hash_init(&functions, 8, NULL, NULL, 0); - for (zend_hash_internal_pointer_reset_ex(EG(function_table), &position); - zend_hash_get_current_data_ex(EG(function_table), (void**)&zf, &position) == SUCCESS; - zend_hash_move_forward_ex(EG(function_table), &position)) { - - if (zf->type == ZEND_USER_FUNCTION) { - zend_hash_next_index_insert( - &functions, (void**) &zf, sizeof(zend_function), NULL); + phpdbg_try_access { + for (zend_hash_internal_pointer_reset_ex(EG(function_table), &position); + zend_hash_get_current_data_ex(EG(function_table), (void**)&zf, &position) == SUCCESS; + zend_hash_move_forward_ex(EG(function_table), &position)) { + if (zf->type == ZEND_USER_FUNCTION) { + zend_hash_next_index_insert(&functions, (void**) &zf, sizeof(zend_function), NULL); + } } - } + } phpdbg_catch_access { + phpdbg_notice("Not all functions could be fetched, possibly invalid data source"); + } phpdbg_end_try_access(); phpdbg_notice("User Functions (%d)", zend_hash_num_elements(&functions)); diff --git a/phpdbg_list.c b/phpdbg_list.c index e8db4e605c..5757d33dda 100644 --- a/phpdbg_list.c +++ b/phpdbg_list.c @@ -34,14 +34,14 @@ ZEND_EXTERN_MODULE_GLOBALS(phpdbg); -#define PHPDBG_LIST_COMMAND_D(f, h, a, m, l, s) \ - PHPDBG_COMMAND_D_EXP(f, h, a, m, l, s, &phpdbg_prompt_commands[13]) +#define PHPDBG_LIST_COMMAND_D(f, h, a, m, l, s, flags) \ + PHPDBG_COMMAND_D_EXP(f, h, a, m, l, s, &phpdbg_prompt_commands[13], flags) const phpdbg_command_t phpdbg_list_commands[] = { - PHPDBG_LIST_COMMAND_D(lines, "lists the specified lines", 'l', list_lines, NULL, "l"), - PHPDBG_LIST_COMMAND_D(class, "lists the specified class", 'c', list_class, NULL, "s"), - PHPDBG_LIST_COMMAND_D(method, "lists the specified method", 'm', list_method, NULL, "m"), - PHPDBG_LIST_COMMAND_D(func, "lists the specified function", 'f', list_func, NULL, "s"), + PHPDBG_LIST_COMMAND_D(lines, "lists the specified lines", 'l', list_lines, NULL, "l", PHPDBG_ASYNC_SAFE), + PHPDBG_LIST_COMMAND_D(class, "lists the specified class", 'c', list_class, NULL, "s", PHPDBG_ASYNC_SAFE), + PHPDBG_LIST_COMMAND_D(method, "lists the specified method", 'm', list_method, NULL, "m", PHPDBG_ASYNC_SAFE), + PHPDBG_LIST_COMMAND_D(func, "lists the specified function", 'f', list_func, NULL, "s", PHPDBG_ASYNC_SAFE), PHPDBG_END_COMMAND }; @@ -81,7 +81,7 @@ PHPDBG_LIST(method) /* {{{ */ { zend_class_entry **ce; - if (zend_lookup_class(param->method.class, strlen(param->method.class), &ce TSRMLS_CC) == SUCCESS) { + if (phpdbg_safe_class_lookup(param->method.class, strlen(param->method.class), &ce TSRMLS_CC) == SUCCESS) { zend_function *function; char *lcname = zend_str_tolower_dup(param->method.name, strlen(param->method.name)); @@ -103,7 +103,7 @@ PHPDBG_LIST(class) /* {{{ */ { zend_class_entry **ce; - if (zend_lookup_class(param->str, param->len, &ce TSRMLS_CC) == SUCCESS) { + if (phpdbg_safe_class_lookup(param->str, param->len, &ce TSRMLS_CC) == SUCCESS) { if ((*ce)->type == ZEND_USER_CLASS) { if ((*ce)->info.user.filename) { phpdbg_list_file( @@ -124,59 +124,41 @@ PHPDBG_LIST(class) /* {{{ */ return SUCCESS; } /* }}} */ -void phpdbg_list_file(const char *filename, long count, long offset, int highlight TSRMLS_DC) /* {{{ */ +void phpdbg_list_file(const char *filename, uint count, uint offset, uint highlight TSRMLS_DC) /* {{{ */ { - struct stat st; - char *opened = NULL; - char buffer[8096] = {0,}; - long line = 0; - - php_stream *stream = NULL; - - if (VCWD_STAT(filename, &st) == FAILURE) { - phpdbg_error("Failed to stat file %s", filename); - return; - } + uint line, lastline; + phpdbg_file_source **data; - stream = php_stream_open_wrapper(filename, "rb", USE_PATH, &opened); - - if (!stream) { - phpdbg_error("Failed to open file %s to list", filename); - return; + if (zend_hash_find(&PHPDBG_G(file_sources), filename, strlen(filename), (void **) &data) == FAILURE) { + phpdbg_error("Could not find information about included file..."); } - + if (offset < 0) { count += offset; offset = 0; } - - while (php_stream_gets(stream, buffer, sizeof(buffer)) != NULL) { - long linelen = strlen(buffer); - - ++line; - - if (offset <= line) { - if (!highlight) { - phpdbg_write("%05ld: %s", line, buffer); - } else { - if (highlight != line) { - phpdbg_write(" %05ld: %s", line, buffer); - } else { - phpdbg_write(">%05ld: %s", line, buffer); - } - } - if (buffer[linelen - 1] != '\n') { - phpdbg_write("\n"); - } - } + lastline = offset + count; - if (count > 0 && count + offset - 1 < line) { - break; + if (lastline > (*data)->lines) { + lastline = (*data)->lines; + } + + for (line = offset; line < lastline;) { + uint linestart = (*data)->line[line++]; + uint linelen = (*data)->line[line] - linestart; + char *buffer = (*data)->buf + linestart; + + if (!highlight) { + phpdbg_write("%05u: %.*s", line, linelen, buffer); + } else { + if (highlight != line) { + phpdbg_write(" %05u: %.*s", line, linelen, buffer); + } else { + phpdbg_write(">%05u: %.*s", line, linelen, buffer); + } } } - - php_stream_close(stream); } /* }}} */ void phpdbg_list_function(const zend_function *fbc TSRMLS_DC) /* {{{ */ @@ -188,10 +170,9 @@ void phpdbg_list_function(const zend_function *fbc TSRMLS_DC) /* {{{ */ return; } - ops = (zend_op_array*)fbc; + ops = (zend_op_array *)fbc; - phpdbg_list_file(ops->filename, - ops->line_end - ops->line_start + 1, ops->line_start, 0 TSRMLS_CC); + phpdbg_list_file(ops->filename, ops->line_end - ops->line_start + 1, ops->line_start, 0 TSRMLS_CC); } /* }}} */ void phpdbg_list_function_byname(const char *str, size_t len TSRMLS_DC) /* {{{ */ @@ -222,12 +203,83 @@ void phpdbg_list_function_byname(const char *str, size_t len TSRMLS_DC) /* {{{ * /* use lowercase names, case insensitive */ func_name = zend_str_tolower_dup(func_name, func_name_len); - if (zend_hash_find(func_table, func_name, func_name_len+1, (void**)&fbc) == SUCCESS) { - phpdbg_list_function(fbc TSRMLS_CC); - } else { - phpdbg_error("Function %s not found", func_name); - } + phpdbg_try_access { + if (zend_hash_find(func_table, func_name, func_name_len+1, (void**)&fbc) == SUCCESS) { + phpdbg_list_function(fbc TSRMLS_CC); + } else { + phpdbg_error("Function %s not found", func_name); + } + } phpdbg_catch_access { + phpdbg_error("Could not list function %s, invalid data source", func_name); + } phpdbg_end_try_access(); efree(func_name); } /* }}} */ +zend_op_array *phpdbg_compile_file(zend_file_handle *file, int type TSRMLS_DC) { + phpdbg_file_source data, *dataptr; + zend_file_handle fake = {0}; + zend_op_array *ret; + char *filename = (char *)(file->opened_path ? file->opened_path : file->filename); + uint line; + char *bufptr, *endptr; + + zend_stream_fixup(file, &data.buf, &data.len TSRMLS_CC); + + data.filename = filename; + data.line[0] = 0; + + if (file->handle.stream.mmap.old_closer) { + /* do not unmap */ + file->handle.stream.closer = file->handle.stream.mmap.old_closer; + } + +#if HAVE_MMAP + if (file->handle.stream.mmap.map) { + data.map = file->handle.stream.mmap.map; + } +#endif + + fake.type = ZEND_HANDLE_MAPPED; + fake.handle.stream.mmap.buf = data.buf; + fake.handle.stream.mmap.len = data.len; + fake.free_filename = 0; + fake.opened_path = NULL; + fake.filename = filename; + + *(dataptr = emalloc(sizeof(phpdbg_file_source) + sizeof(uint) * data.len)) = data; + zend_hash_add(&PHPDBG_G(file_sources), filename, strlen(filename), &dataptr, sizeof(phpdbg_file_source *), NULL); + + for (line = 0, bufptr = data.buf - 1, endptr = data.buf + data.len; ++bufptr < endptr;) { + if (*bufptr == '\n') { + dataptr->line[++line] = (uint)(bufptr - data.buf) + 1; + } + } + dataptr = erealloc(dataptr, sizeof(phpdbg_file_source) + sizeof(uint) * line); + dataptr->lines = ++line; + + ret = PHPDBG_G(compile_file)(&fake, type TSRMLS_CC); + + zend_file_handle_dtor(&fake TSRMLS_CC); + + return ret; +} + +void phpdbg_free_file_source(phpdbg_file_source *data) { +#if HAVE_MMAP + if (data->map) { + munmap(data->map, data->len + ZEND_MMAP_AHEAD); + } else +#endif + if (data->buf) { + efree(data->buf); + } + + efree(data); +} + +void phpdbg_init_list(TSRMLS_D) { + PHPDBG_G(compile_file) = zend_compile_file; + zend_hash_init(&PHPDBG_G(file_sources), 1, NULL, (dtor_func_t) phpdbg_free_file_source, 0); + zend_compile_file = phpdbg_compile_file; +} diff --git a/phpdbg_list.h b/phpdbg_list.h index 14905f6567..385ae20580 100644 --- a/phpdbg_list.h +++ b/phpdbg_list.h @@ -34,8 +34,21 @@ PHPDBG_LIST(func); void phpdbg_list_function_byname(const char *, size_t TSRMLS_DC); void phpdbg_list_function(const zend_function* TSRMLS_DC); -void phpdbg_list_file(const char*, long, long, int TSRMLS_DC); +void phpdbg_list_file(const char*, uint, uint, uint TSRMLS_DC); extern const phpdbg_command_t phpdbg_list_commands[]; +void phpdbg_init_list(TSRMLS_D); + +typedef struct { + char *filename; + char *buf; + size_t len; +#if HAVE_MMAP + void *map; +#endif + uint lines; + uint line[1]; +} phpdbg_file_source; + #endif /* PHPDBG_LIST_H */ diff --git a/phpdbg_print.c b/phpdbg_print.c index 76321a5042..e2b4dd229c 100644 --- a/phpdbg_print.c +++ b/phpdbg_print.c @@ -26,16 +26,16 @@ ZEND_EXTERN_MODULE_GLOBALS(phpdbg); -#define PHPDBG_PRINT_COMMAND_D(f, h, a, m, l, s) \ - PHPDBG_COMMAND_D_EXP(f, h, a, m, l, s, &phpdbg_prompt_commands[9]) +#define PHPDBG_PRINT_COMMAND_D(f, h, a, m, l, s, flags) \ + PHPDBG_COMMAND_D_EXP(f, h, a, m, l, s, &phpdbg_prompt_commands[9], flags) const phpdbg_command_t phpdbg_print_commands[] = { - PHPDBG_PRINT_COMMAND_D(exec, "print out the instructions in the execution context", 'e', print_exec, NULL, 0), - PHPDBG_PRINT_COMMAND_D(opline, "print out the instruction in the current opline", 'o', print_opline, NULL, 0), - PHPDBG_PRINT_COMMAND_D(class, "print out the instructions in the specified class", 'c', print_class, NULL, "s"), - PHPDBG_PRINT_COMMAND_D(method, "print out the instructions in the specified method", 'm', print_method, NULL, "m"), - PHPDBG_PRINT_COMMAND_D(func, "print out the instructions in the specified function", 'f', print_func, NULL, "s"), - PHPDBG_PRINT_COMMAND_D(stack, "print out the instructions in the current stack", 's', print_stack, NULL, 0), + PHPDBG_PRINT_COMMAND_D(exec, "print out the instructions in the execution context", 'e', print_exec, NULL, 0, PHPDBG_ASYNC_SAFE), + PHPDBG_PRINT_COMMAND_D(opline, "print out the instruction in the current opline", 'o', print_opline, NULL, 0, PHPDBG_ASYNC_SAFE), + PHPDBG_PRINT_COMMAND_D(class, "print out the instructions in the specified class", 'c', print_class, NULL, "s", PHPDBG_ASYNC_SAFE), + PHPDBG_PRINT_COMMAND_D(method, "print out the instructions in the specified method", 'm', print_method, NULL, "m", PHPDBG_ASYNC_SAFE), + PHPDBG_PRINT_COMMAND_D(func, "print out the instructions in the specified function", 'f', print_func, NULL, "s", PHPDBG_ASYNC_SAFE), + PHPDBG_PRINT_COMMAND_D(stack, "print out the instructions in the current stack", 's', print_stack, NULL, 0, PHPDBG_ASYNC_SAFE), PHPDBG_END_COMMAND }; @@ -108,7 +108,7 @@ static inline void phpdbg_print_function_helper(zend_function *method TSRMLS_DC) PHPDBG_PRINT(exec) /* {{{ */ { if (PHPDBG_G(exec)) { - if (!PHPDBG_G(ops)) { + if (!PHPDBG_G(ops) && !(PHPDBG_G(flags) & PHPDBG_IN_SIGNAL_HANDLER)) { phpdbg_compile(TSRMLS_C); } @@ -154,7 +154,7 @@ PHPDBG_PRINT(class) /* {{{ */ { zend_class_entry **ce; - if (zend_lookup_class(param->str, param->len, &ce TSRMLS_CC) == SUCCESS) { + if (phpdbg_safe_class_lookup(param->str, param->len, &ce TSRMLS_CC) == SUCCESS) { phpdbg_notice("%s %s: %s", ((*ce)->type == ZEND_USER_CLASS) ? "User" : "Internal", @@ -187,7 +187,7 @@ PHPDBG_PRINT(method) /* {{{ */ { zend_class_entry **ce; - if (zend_lookup_class(param->method.class, strlen(param->method.class), &ce TSRMLS_CC) == SUCCESS) { + if (phpdbg_safe_class_lookup(param->method.class, strlen(param->method.class), &ce TSRMLS_CC) == SUCCESS) { zend_function *fbc; char *lcname = zend_str_tolower_dup(param->method.name, strlen(param->method.name)); @@ -234,18 +234,22 @@ PHPDBG_PRINT(func) /* {{{ */ func_table = EG(function_table); } - lcname = zend_str_tolower_dup(func_name, func_name_len); + lcname = zend_str_tolower_dup(func_name, func_name_len); - if (zend_hash_find(func_table, lcname, strlen(lcname)+1, (void**)&fbc) == SUCCESS) { - phpdbg_notice("%s %s %s", - (fbc->type == ZEND_USER_FUNCTION) ? "User" : "Internal", - (fbc->common.scope) ? "Method" : "Function", - fbc->common.function_name); + phpdbg_try_access { + if (zend_hash_find(func_table, lcname, func_name_len + 1, (void **) &fbc) == SUCCESS) { + phpdbg_notice("%s %s %s", + (fbc->type == ZEND_USER_FUNCTION) ? "User" : "Internal", + (fbc->common.scope) ? "Method" : "Function", + fbc->common.function_name); - phpdbg_print_function_helper(fbc TSRMLS_CC); - } else { - phpdbg_error("The function %s could not be found", func_name); - } + phpdbg_print_function_helper(fbc TSRMLS_CC); + } else { + phpdbg_error("The function %s could not be found", func_name); + } + } phpdbg_catch_access { + phpdbg_error("Couldn't fetch function %.*s, invalid data source", func_name_len, func_name); + } phpdbg_end_try_access(); efree(lcname); diff --git a/phpdbg_prompt.c b/phpdbg_prompt.c index 367926abd5..7bb8407730 100644 --- a/phpdbg_prompt.c +++ b/phpdbg_prompt.c @@ -40,30 +40,30 @@ /* {{{ command declarations */ const phpdbg_command_t phpdbg_prompt_commands[] = { - PHPDBG_COMMAND_D(exec, "set execution context", 'e', NULL, "s"), - PHPDBG_COMMAND_D(step, "step through execution", 's', NULL, 0), - PHPDBG_COMMAND_D(continue,"continue execution", 'c', NULL, 0), - PHPDBG_COMMAND_D(run, "attempt execution", 'r', NULL, "|s"), - PHPDBG_COMMAND_D(ev, "evaluate some code", 0, NULL, "i"), - PHPDBG_COMMAND_D(until, "continue past the current line", 'u', NULL, 0), - PHPDBG_COMMAND_D(finish, "continue past the end of the stack", 'F', NULL, 0), - PHPDBG_COMMAND_D(leave, "continue until the end of the stack", 'L', NULL, 0), - PHPDBG_COMMAND_D(print, "print something", 'p', phpdbg_print_commands, 0), - PHPDBG_COMMAND_D(break, "set breakpoint", 'b', phpdbg_break_commands, "|*c"), - PHPDBG_COMMAND_D(back, "show trace", 't', NULL, "|n"), - PHPDBG_COMMAND_D(frame, "switch to a frame", 'f', NULL, "|n"), - PHPDBG_COMMAND_D(list, "lists some code", 'l', phpdbg_list_commands, "*"), - PHPDBG_COMMAND_D(info, "displays some informations", 'i', phpdbg_info_commands, "s"), - PHPDBG_COMMAND_D(clean, "clean the execution environment", 'X', NULL, 0), - PHPDBG_COMMAND_D(clear, "clear breakpoints", 'C', NULL, 0), - PHPDBG_COMMAND_D(help, "show help menu", 'h', phpdbg_help_commands, "|s"), - PHPDBG_COMMAND_D(set, "set phpdbg configuration", 'S', phpdbg_set_commands, "s"), - PHPDBG_COMMAND_D(register,"register a function", 'R', NULL, "s"), - PHPDBG_COMMAND_D(source, "execute a phpdbginit", '<', NULL, "s"), - PHPDBG_COMMAND_D(export, "export breaks to a .phpdbginit script", '>', NULL, "s"), - PHPDBG_COMMAND_D(sh, "shell a command", 0, NULL, "i"), - PHPDBG_COMMAND_D(quit, "exit phpdbg", 'q', NULL, 0), - PHPDBG_COMMAND_D(watch, "set watchpoint", 'w', phpdbg_watch_commands, "|ss"), + PHPDBG_COMMAND_D(exec, "set execution context", 'e', NULL, "s", 0), + PHPDBG_COMMAND_D(step, "step through execution", 's', NULL, 0, PHPDBG_ASYNC_SAFE), + PHPDBG_COMMAND_D(continue,"continue execution", 'c', NULL, 0, PHPDBG_ASYNC_SAFE), + PHPDBG_COMMAND_D(run, "attempt execution", 'r', NULL, "|s", 0), + PHPDBG_COMMAND_D(ev, "evaluate some code", 0 , NULL, "i", 0), /* restricted ASYNC_SAFE */ + PHPDBG_COMMAND_D(until, "continue past the current line", 'u', NULL, 0, 0), + PHPDBG_COMMAND_D(finish, "continue past the end of the stack", 'F', NULL, 0, 0), + PHPDBG_COMMAND_D(leave, "continue until the end of the stack", 'L', NULL, 0, 0), + PHPDBG_COMMAND_D(print, "print something", 'p', phpdbg_print_commands, 0, 0), + PHPDBG_COMMAND_D(break, "set breakpoint", 'b', phpdbg_break_commands, "|*c", 0), + PHPDBG_COMMAND_D(back, "show trace", 't', NULL, "|n", PHPDBG_ASYNC_SAFE), + PHPDBG_COMMAND_D(frame, "switch to a frame", 'f', NULL, "|n", PHPDBG_ASYNC_SAFE), + PHPDBG_COMMAND_D(list, "lists some code", 'l', phpdbg_list_commands, "*", PHPDBG_ASYNC_SAFE), + PHPDBG_COMMAND_D(info, "displays some informations", 'i', phpdbg_info_commands, "s", PHPDBG_ASYNC_SAFE), + PHPDBG_COMMAND_D(clean, "clean the execution environment", 'X', NULL, 0, 0), + PHPDBG_COMMAND_D(clear, "clear breakpoints", 'C', NULL, 0, 0), + PHPDBG_COMMAND_D(help, "show help menu", 'h', phpdbg_help_commands, "|s", PHPDBG_ASYNC_SAFE), + PHPDBG_COMMAND_D(set, "set phpdbg configuration", 'S', phpdbg_set_commands, "s", PHPDBG_ASYNC_SAFE), + PHPDBG_COMMAND_D(register,"register a function", 'R', NULL, "s", 0), + PHPDBG_COMMAND_D(source, "execute a phpdbginit", '<', NULL, "s", 0), + PHPDBG_COMMAND_D(export, "export breaks to a .phpdbginit script", '>', NULL, "s", PHPDBG_ASYNC_SAFE), + PHPDBG_COMMAND_D(sh, "shell a command", 0 , NULL, "i", 0), + PHPDBG_COMMAND_D(quit, "exit phpdbg", 'q', NULL, 0, PHPDBG_ASYNC_SAFE), + PHPDBG_COMMAND_D(watch, "set watchpoint", 'w', phpdbg_watch_commands, "|ss", 0), PHPDBG_END_COMMAND }; /* }}} */ @@ -250,21 +250,17 @@ void phpdbg_try_file_init(char *init_file, size_t init_file_len, zend_bool free_ phpdbg_init_param(&stack, STACK_PARAM); if (phpdbg_do_parse(&stack, input TSRMLS_CC) <= 0) { - switch (phpdbg_stack_execute(&stack, &why TSRMLS_CC)) { + switch (phpdbg_stack_execute(&stack, &why, 1 /* allow_async_unsafe == 1 */ TSRMLS_CC)) { case FAILURE: -// if (!(PHPDBG_G(flags) & PHPDBG_IS_QUITTING)) { - if (phpdbg_call_register(&stack TSRMLS_CC) == FAILURE) { - phpdbg_error( - "Unrecognized command in %s:%d: %s, %s!", - init_file, line, input, why); - } -// } + if (phpdbg_call_register(&stack TSRMLS_CC) == FAILURE) { + phpdbg_error("Unrecognized command in %s:%d: %s, %s!", init_file, line, input, why); + } break; } } if (why) { - free(why); + efree(why); why = NULL; } @@ -992,7 +988,7 @@ PHPDBG_COMMAND(watch) /* {{{ */ return SUCCESS; } /* }}} */ -int phpdbg_interactive(TSRMLS_D) /* {{{ */ +int phpdbg_interactive(zend_bool allow_async_unsafe TSRMLS_DC) /* {{{ */ { int ret = SUCCESS; char *why = NULL; @@ -1008,10 +1004,10 @@ int phpdbg_interactive(TSRMLS_D) /* {{{ */ phpdbg_init_param(&stack, STACK_PARAM); if (phpdbg_do_parse(&stack, input TSRMLS_CC) <= 0) { - switch (ret = phpdbg_stack_execute(&stack, &why TSRMLS_CC)) { + switch (ret = phpdbg_stack_execute(&stack, &why, allow_async_unsafe TSRMLS_CC)) { case FAILURE: if (!(PHPDBG_G(flags) & PHPDBG_IS_QUITTING)) { - if (phpdbg_call_register(&stack TSRMLS_CC) == FAILURE) { + if (!allow_async_unsafe || phpdbg_call_register(&stack TSRMLS_CC) == FAILURE) { if (why) { phpdbg_error("%s", why); } @@ -1019,7 +1015,7 @@ int phpdbg_interactive(TSRMLS_D) /* {{{ */ } if (why) { - free(why); + efree(why); why = NULL; } break; @@ -1037,13 +1033,12 @@ int phpdbg_interactive(TSRMLS_D) /* {{{ */ } if (why) { - free(why); + efree(why); why = NULL; } phpdbg_stack_free(&stack); phpdbg_destroy_input(&input TSRMLS_CC); - } while ((input = phpdbg_read_input(NULL TSRMLS_CC))); } @@ -1054,7 +1049,7 @@ out: } if (why) { - free(why); + efree(why); } if (EG(in_execution)) { @@ -1145,6 +1140,27 @@ static inline zend_execute_data *phpdbg_create_execute_data(zend_op_array *op_ar #endif } /* }}} */ +#define DO_INTERACTIVE(allow_async_unsafe) do { \ + if (!(PHPDBG_G(flags) & PHPDBG_IN_EVAL)) { \ + phpdbg_list_file( \ + zend_get_executed_filename(TSRMLS_C), \ + 3, \ + zend_get_executed_lineno(TSRMLS_C)-1, \ + zend_get_executed_lineno(TSRMLS_C) \ + TSRMLS_CC \ + ); \ + } \ + \ + switch (phpdbg_interactive(allow_async_unsafe TSRMLS_CC)) { \ + case PHPDBG_LEAVE: \ + case PHPDBG_FINISH: \ + case PHPDBG_UNTIL: \ + case PHPDBG_NEXT:{ \ + goto next; \ + } \ + } \ +} while (0) + #if PHP_VERSION_ID >= 50500 void phpdbg_execute_ex(zend_execute_data *execute_data TSRMLS_DC) /* {{{ */ { @@ -1181,41 +1197,17 @@ zend_vm_enter: #endif while (1) { - if ((PHPDBG_G(flags) & PHPDBG_BP_RESOLVE_MASK)) { /* resolve nth opline breakpoints */ phpdbg_resolve_op_array_breaks(EG(active_op_array) TSRMLS_CC); } - + #ifdef ZEND_WIN32 if (EG(timed_out)) { zend_timeout(0); } #endif -#define DO_INTERACTIVE() do { \ - if (!(PHPDBG_G(flags) & PHPDBG_IN_EVAL)) { \ - phpdbg_list_file( \ - zend_get_executed_filename(TSRMLS_C), \ - 3, \ - zend_get_executed_lineno(TSRMLS_C)-1, \ - zend_get_executed_lineno(TSRMLS_C) \ - TSRMLS_CC \ - ); \ - } \ - \ -/* do { */\ - switch (phpdbg_interactive(TSRMLS_C)) { \ - case PHPDBG_LEAVE: \ - case PHPDBG_FINISH: \ - case PHPDBG_UNTIL: \ - case PHPDBG_NEXT:{ \ - goto next; \ - } \ - } \ -/* } while (!(PHPDBG_G(flags) & PHPDBG_IS_QUITTING)); */\ -} while (0) - /* allow conditional breakpoints and initialization to access the vm uninterrupted */ if ((PHPDBG_G(flags) & PHPDBG_IN_COND_BP) || @@ -1263,7 +1255,7 @@ zend_vm_enter: zend_get_executed_filename(TSRMLS_C), zend_get_executed_lineno(TSRMLS_C) ); - DO_INTERACTIVE(); + DO_INTERACTIVE(1); } else { /* skip possible breakpoints */ goto next; @@ -1277,13 +1269,13 @@ zend_vm_enter: if (PHPDBG_G(flags) & PHPDBG_IS_STEPPING && (PHPDBG_G(flags) & PHPDBG_STEP_OPCODE || execute_data->opline->lineno != PHPDBG_G(last_line))) { PHPDBG_G(flags) &= ~PHPDBG_IS_STEPPING; - DO_INTERACTIVE(); + DO_INTERACTIVE(1); } /* check if some watchpoint was hit */ { if (phpdbg_print_changed_zvals(TSRMLS_C) == SUCCESS) { - DO_INTERACTIVE(); + DO_INTERACTIVE(1); } } @@ -1295,18 +1287,19 @@ zend_vm_enter: && (brake = phpdbg_find_breakpoint(execute_data TSRMLS_CC)) && (brake->type != PHPDBG_BREAK_FILE || execute_data->opline->lineno != PHPDBG_G(last_line))) { phpdbg_hit_breakpoint(brake, 1 TSRMLS_CC); - DO_INTERACTIVE(); + DO_INTERACTIVE(1); } } -next: if (PHPDBG_G(flags) & PHPDBG_IS_SIGNALED) { phpdbg_writeln(EMPTY); phpdbg_notice("Program received signal SIGINT"); - PHPDBG_G(flags) &= ~PHPDBG_IS_SIGNALED; - DO_INTERACTIVE(); + DO_INTERACTIVE(1); } + PHPDBG_G(flags) &= ~PHPDBG_IS_SIGNALED; +next: + PHPDBG_G(last_line) = execute_data->opline->lineno; PHPDBG_G(vmret) = execute_data->opline->handler(execute_data TSRMLS_CC); @@ -1335,9 +1328,12 @@ next: zend_error_noreturn(E_ERROR, "Arrived at end of main loop which shouldn't happen"); } /* }}} */ +/* only if *not* interactive and while executing */ void phpdbg_force_interruption(TSRMLS_D) { zend_execute_data *data = EG(current_execute_data); /* should be always readable if not NULL */ + PHPDBG_G(flags) |= PHPDBG_IN_SIGNAL_HANDLER; + if (data) { if (data->op_array) { phpdbg_notice("Current opline: %p (op #%lu) in %s:%u", data->opline, (data->opline - data->op_array->opcodes) / sizeof(data->opline), data->op_array->filename, data->opline->lineno); @@ -1347,4 +1343,13 @@ void phpdbg_force_interruption(TSRMLS_D) { } else { phpdbg_notice("No information available about executing context"); } + + DO_INTERACTIVE(0); + +next: + PHPDBG_G(flags) &= ~PHPDBG_IN_SIGNAL_HANDLER; + + if (PHPDBG_G(flags) & PHPDBG_IS_QUITTING) { + zend_bailout(); + } } diff --git a/phpdbg_prompt.h b/phpdbg_prompt.h index 5909407e7d..4247a44088 100644 --- a/phpdbg_prompt.h +++ b/phpdbg_prompt.h @@ -24,9 +24,11 @@ /* {{{ */ void phpdbg_init(char *init_file, size_t init_file_len, zend_bool use_default TSRMLS_DC); void phpdbg_try_file_init(char *init_file, size_t init_file_len, zend_bool free_init TSRMLS_DC); -int phpdbg_interactive(TSRMLS_D); +int phpdbg_interactive(zend_bool allow_async_unsafe TSRMLS_DC); int phpdbg_compile(TSRMLS_D); -void phpdbg_clean(zend_bool full TSRMLS_DC); /* }}} */ +void phpdbg_clean(zend_bool full TSRMLS_DC); +void phpdbg_force_interruption(TSRMLS_D); +/* }}} */ /* {{{ phpdbg command handlers */ PHPDBG_COMMAND(exec); @@ -64,6 +66,4 @@ void phpdbg_execute_ex(zend_execute_data *execute_data TSRMLS_DC); void phpdbg_execute_ex(zend_op_array *op_array TSRMLS_DC); #endif /* }}} */ -void phpdbg_force_interruption(TSRMLS_D); - #endif /* PHPDBG_PROMPT_H */ diff --git a/phpdbg_set.c b/phpdbg_set.c index 54269a8193..e7699a0c77 100644 --- a/phpdbg_set.c +++ b/phpdbg_set.c @@ -27,21 +27,21 @@ ZEND_EXTERN_MODULE_GLOBALS(phpdbg); -#define PHPDBG_SET_COMMAND_D(f, h, a, m, l, s) \ - PHPDBG_COMMAND_D_EXP(f, h, a, m, l, s, &phpdbg_prompt_commands[18]) +#define PHPDBG_SET_COMMAND_D(f, h, a, m, l, s, flags) \ + PHPDBG_COMMAND_D_EXP(f, h, a, m, l, s, &phpdbg_prompt_commands[18], flags) const phpdbg_command_t phpdbg_set_commands[] = { - PHPDBG_SET_COMMAND_D(prompt, "usage: set prompt []", 'p', set_prompt, NULL, "|s"), + PHPDBG_SET_COMMAND_D(prompt, "usage: set prompt []", 'p', set_prompt, NULL, "|s", 0), #ifndef _WIN32 - PHPDBG_SET_COMMAND_D(color, "usage: set color ", 'c', set_color, NULL, "ss"), - PHPDBG_SET_COMMAND_D(colors, "usage: set colors []", 'C', set_colors, NULL, "|b"), + PHPDBG_SET_COMMAND_D(color, "usage: set color ", 'c', set_color, NULL, "ss", PHPDBG_ASYNC_SAFE), + PHPDBG_SET_COMMAND_D(colors, "usage: set colors []", 'C', set_colors, NULL, "|b", PHPDBG_ASYNC_SAFE), #endif - PHPDBG_SET_COMMAND_D(oplog, "usage: set oplog []", 'O', set_oplog, NULL, "|s"), - PHPDBG_SET_COMMAND_D(break, "usage: set break id []", 'b', set_break, NULL, "l|b"), - PHPDBG_SET_COMMAND_D(breaks, "usage: set breaks []", 'B', set_breaks, NULL, "|b"), - PHPDBG_SET_COMMAND_D(quiet, "usage: set quiet []", 'q', set_quiet, NULL, "|b"), - PHPDBG_SET_COMMAND_D(stepping, "usage: set stepping []", 's', set_stepping, NULL, "|s"), - PHPDBG_SET_COMMAND_D(refcount, "usage: set refcount []", 'r', set_refcount, NULL, "|b"), + PHPDBG_SET_COMMAND_D(oplog, "usage: set oplog []", 'O', set_oplog, NULL, "|s", 0), + PHPDBG_SET_COMMAND_D(break, "usage: set break id []", 'b', set_break, NULL, "l|b", PHPDBG_ASYNC_SAFE), + PHPDBG_SET_COMMAND_D(breaks, "usage: set breaks []", 'B', set_breaks, NULL, "|b", PHPDBG_ASYNC_SAFE), + PHPDBG_SET_COMMAND_D(quiet, "usage: set quiet []", 'q', set_quiet, NULL, "|b", PHPDBG_ASYNC_SAFE), + PHPDBG_SET_COMMAND_D(stepping, "usage: set stepping []", 's', set_stepping, NULL, "|s", PHPDBG_ASYNC_SAFE), + PHPDBG_SET_COMMAND_D(refcount, "usage: set refcount []", 'r', set_refcount, NULL, "|b", PHPDBG_ASYNC_SAFE), PHPDBG_END_COMMAND }; diff --git a/phpdbg_sigsafe.c b/phpdbg_sigsafe.c index 96ac713f23..7238ad102f 100644 --- a/phpdbg_sigsafe.c +++ b/phpdbg_sigsafe.c @@ -96,9 +96,13 @@ void phpdbg_set_sigsafe_mem(char *buffer TSRMLS_DC) { mem->heap.reserve_size = 0; } +zend_mm_heap *phpdbg_original_heap_sigsafe_mem(TSRMLS_D) { + return PHPDBG_G(sigsafe_mem).old_heap; +} + void phpdbg_clear_sigsafe_mem(TSRMLS_D) { + zend_mm_set_heap(phpdbg_original_heap_sigsafe_mem(TSRMLS_C) TSRMLS_CC); PHPDBG_G(sigsafe_mem).mem = NULL; - zend_mm_set_heap(PHPDBG_G(sigsafe_mem).old_heap TSRMLS_CC); } zend_bool phpdbg_active_sigsafe_mem(TSRMLS_D) { diff --git a/phpdbg_sigsafe.h b/phpdbg_sigsafe.h index 810e07c789..6ed74f53db 100644 --- a/phpdbg_sigsafe.h +++ b/phpdbg_sigsafe.h @@ -7,7 +7,7 @@ typedef struct { char *mem; - zend_bool allocated;; + zend_bool allocated; zend_mm_heap heap; zend_mm_heap *old_heap; zend_mm_storage storage; @@ -20,4 +20,6 @@ zend_bool phpdbg_active_sigsafe_mem(TSRMLS_D); void phpdbg_set_sigsafe_mem(char *mem TSRMLS_DC); void phpdbg_clear_sigsafe_mem(TSRMLS_D); +zend_mm_heap *phpdbg_original_heap_sigsafe_mem(TSRMLS_D); + #endif diff --git a/phpdbg_utils.c b/phpdbg_utils.c index 4d3eb4cde6..d12a304d12 100644 --- a/phpdbg_utils.c +++ b/phpdbg_utils.c @@ -475,3 +475,34 @@ PHPDBG_API void phpdbg_set_async_io(int fd) { fcntl(STDIN_FILENO, F_SETFL, flags | FASYNC); #endif } + +int phpdbg_safe_class_lookup(const char *name, int name_length, zend_class_entry ***ce TSRMLS_DC) { + if (PHPDBG_G(flags) & PHPDBG_IN_SIGNAL_HANDLER) { + char *lc_name, *lc_free; + int lc_length, ret = FAILURE; + + if (name == NULL || !name_length) { + return FAILURE; + } + + lc_free = lc_name = emalloc(name_length + 1); + zend_str_tolower_copy(lc_name, name, name_length); + lc_length = name_length + 1; + + if (lc_name[0] == '\\') { + lc_name += 1; + lc_length -= 1; + } + + phpdbg_try_access { + ret = zend_hash_find(EG(class_table), lc_name, lc_length, (void **) &ce); + } phpdbg_catch_access { + phpdbg_error("Could not fetch class %.*s, invalid data source", name_length, name); + } phpdbg_end_try_access(); + + efree(lc_free); + return ret; + } else { + return zend_lookup_class(name, name_length, ce TSRMLS_CC); + } +} diff --git a/phpdbg_utils.h b/phpdbg_utils.h index 9686d76948..01aa8490fe 100644 --- a/phpdbg_utils.h +++ b/phpdbg_utils.h @@ -146,4 +146,6 @@ static void zend_hash_get_current_key_zval_ex(const HashTable *ht, zval *key, Ha } #endif +int phpdbg_safe_class_lookup(const char *name, int name_length, zend_class_entry ***ce TSRMLS_DC); + #endif /* PHPDBG_UTILS_H */ diff --git a/phpdbg_watch.h b/phpdbg_watch.h index d00bcff77e..5c0079057c 100644 --- a/phpdbg_watch.h +++ b/phpdbg_watch.h @@ -42,9 +42,9 @@ PHPDBG_WATCH(recursive); */ static const phpdbg_command_t phpdbg_watch_commands[] = { - PHPDBG_COMMAND_D_EX(array, "create watchpoint on an array", 'a', watch_array, NULL, "s"), - PHPDBG_COMMAND_D_EX(delete, "delete watchpoint", 'd', watch_delete, NULL, "s"), - PHPDBG_COMMAND_D_EX(recursive, "create recursive watchpoints", 'r', watch_recursive, NULL, "s"), + PHPDBG_COMMAND_D_EX(array, "create watchpoint on an array", 'a', watch_array, NULL, "s", 0), + PHPDBG_COMMAND_D_EX(delete, "delete watchpoint", 'd', watch_delete, NULL, "s", 0), + PHPDBG_COMMAND_D_EX(recursive, "create recursive watchpoints", 'r', watch_recursive, NULL, "s", 0), PHPDBG_END_COMMAND };