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.
pg->frame.num = 0;
pg->input_buflen = 0;
pg->sigsafe_mem.mem = NULL;
+ pg->sigsegv_bailout = NULL;
} /* }}} */
static PHP_MINIT_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));
efree(PHPDBG_G(buffer));
PHPDBG_G(buffer) = NULL;
}
-
+
if (PHPDBG_G(exec)) {
efree(PHPDBG_G(exec));
PHPDBG_G(exec) = NULL;
}
do {
- switch (phpdbg_interactive(TSRMLS_C)) {
+ switch (phpdbg_interactive(1 TSRMLS_CC)) {
case PHPDBG_LEAVE:
case PHPDBG_FINISH:
case PHPDBG_UNTIL:
{
int flags;
size_t newlen;
- size_t i;
+ size_t i/*, last_nl*/;
TSRMLS_FETCH();
// if (!(info->si_band & POLLIN)) {
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);
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
zend_activate(TSRMLS_C);
+ phpdbg_init_list(TSRMLS_C);
+
PHPDBG_G(original_free_function) = mm_heap->_free;
mm_heap->_free = phpdbg_watch_efree;
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
/* 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");
+----------------------------------------------------------------------+
| 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 |
#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
#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)
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;
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) /* }}} */
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
};
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)) {
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)) {
} /* }}} */
/* {{{ */
-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;
} /* }}} */
{
efree(*input);
} /* }}} */
-
#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;
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 */
};
/* }}} */
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);
/*
*/
#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
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);
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) {
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(", ");
}
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);
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;
}
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
};
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;
} /* }}} */
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!");
}
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;
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 {
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));
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);
}
}
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;
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);
}
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!");
}
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)) {
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));
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
};
{
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));
{
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(
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) /* {{{ */
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) /* {{{ */
/* 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;
+}
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 */
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
};
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);
}
{
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",
{
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));
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);
/* {{{ 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
}; /* }}} */
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;
}
return SUCCESS;
} /* }}} */
-int phpdbg_interactive(TSRMLS_D) /* {{{ */
+int phpdbg_interactive(zend_bool allow_async_unsafe TSRMLS_DC) /* {{{ */
{
int ret = SUCCESS;
char *why = NULL;
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);
}
}
if (why) {
- free(why);
+ efree(why);
why = NULL;
}
break;
}
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)));
}
}
if (why) {
- free(why);
+ efree(why);
}
if (EG(in_execution)) {
#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) /* {{{ */
{
#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) ||
zend_get_executed_filename(TSRMLS_C),
zend_get_executed_lineno(TSRMLS_C)
);
- DO_INTERACTIVE();
+ DO_INTERACTIVE(1);
} else {
/* skip possible breakpoints */
goto next;
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);
}
}
&& (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);
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);
} 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();
+ }
}
/* {{{ */
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);
void phpdbg_execute_ex(zend_op_array *op_array TSRMLS_DC);
#endif /* }}} */
-void phpdbg_force_interruption(TSRMLS_D);
-
#endif /* PHPDBG_PROMPT_H */
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 [<string>]", 'p', set_prompt, NULL, "|s"),
+ PHPDBG_SET_COMMAND_D(prompt, "usage: set prompt [<string>]", 'p', set_prompt, NULL, "|s", 0),
#ifndef _WIN32
- PHPDBG_SET_COMMAND_D(color, "usage: set color <element> <color>", 'c', set_color, NULL, "ss"),
- PHPDBG_SET_COMMAND_D(colors, "usage: set colors [<on|off>]", 'C', set_colors, NULL, "|b"),
+ PHPDBG_SET_COMMAND_D(color, "usage: set color <element> <color>", 'c', set_color, NULL, "ss", PHPDBG_ASYNC_SAFE),
+ PHPDBG_SET_COMMAND_D(colors, "usage: set colors [<on|off>]", 'C', set_colors, NULL, "|b", PHPDBG_ASYNC_SAFE),
#endif
- PHPDBG_SET_COMMAND_D(oplog, "usage: set oplog [<output>]", 'O', set_oplog, NULL, "|s"),
- PHPDBG_SET_COMMAND_D(break, "usage: set break id [<on|off>]", 'b', set_break, NULL, "l|b"),
- PHPDBG_SET_COMMAND_D(breaks, "usage: set breaks [<on|off>]", 'B', set_breaks, NULL, "|b"),
- PHPDBG_SET_COMMAND_D(quiet, "usage: set quiet [<on|off>]", 'q', set_quiet, NULL, "|b"),
- PHPDBG_SET_COMMAND_D(stepping, "usage: set stepping [<line|op>]", 's', set_stepping, NULL, "|s"),
- PHPDBG_SET_COMMAND_D(refcount, "usage: set refcount [<on|off>]", 'r', set_refcount, NULL, "|b"),
+ PHPDBG_SET_COMMAND_D(oplog, "usage: set oplog [<output>]", 'O', set_oplog, NULL, "|s", 0),
+ PHPDBG_SET_COMMAND_D(break, "usage: set break id [<on|off>]", 'b', set_break, NULL, "l|b", PHPDBG_ASYNC_SAFE),
+ PHPDBG_SET_COMMAND_D(breaks, "usage: set breaks [<on|off>]", 'B', set_breaks, NULL, "|b", PHPDBG_ASYNC_SAFE),
+ PHPDBG_SET_COMMAND_D(quiet, "usage: set quiet [<on|off>]", 'q', set_quiet, NULL, "|b", PHPDBG_ASYNC_SAFE),
+ PHPDBG_SET_COMMAND_D(stepping, "usage: set stepping [<line|op>]", 's', set_stepping, NULL, "|s", PHPDBG_ASYNC_SAFE),
+ PHPDBG_SET_COMMAND_D(refcount, "usage: set refcount [<on|off>]", 'r', set_refcount, NULL, "|b", PHPDBG_ASYNC_SAFE),
PHPDBG_END_COMMAND
};
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) {
typedef struct {
char *mem;
- zend_bool allocated;;
+ zend_bool allocated;
zend_mm_heap heap;
zend_mm_heap *old_heap;
zend_mm_storage storage;
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
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);
+ }
+}
}
#endif
+int phpdbg_safe_class_lookup(const char *name, int name_length, zend_class_entry ***ce TSRMLS_DC);
+
#endif /* PHPDBG_UTILS_H */
*/
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
};