Made some functions async safe and provide interactive shell in signal handler
authorBob Weinand <bobwei9@hotmail.com>
Thu, 2 Oct 2014 23:29:41 +0000 (01:29 +0200)
committerBob Weinand <bobwei9@hotmail.com>
Thu, 2 Oct 2014 23:29:41 +0000 (01:29 +0200)
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.

18 files changed:
phpdbg.c
phpdbg.h
phpdbg_break.c
phpdbg_cmd.c
phpdbg_cmd.h
phpdbg_frame.c
phpdbg_info.c
phpdbg_list.c
phpdbg_list.h
phpdbg_print.c
phpdbg_prompt.c
phpdbg_prompt.h
phpdbg_set.c
phpdbg_sigsafe.c
phpdbg_sigsafe.h
phpdbg_utils.c
phpdbg_utils.h
phpdbg_watch.h

index a72ddbb1839ba6ef0434e14a17dfeb9acf748ef3..15647f2c58babf396563881f21087c4e9538b15b 100644 (file)
--- 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");
index 105614c8691c023755d99209a4e59d010dd52469..941b539d11805d0196bfadeadbd3b8b7677991f7 100644 (file)
--- 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) /* }}} */
 
index be76b22b05965b03e5657174e6f5f2c136d085ff..386d4d95620dcdd2e5fcba88a6031db4f8195df9 100644 (file)
 
 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
 };
 
index 0ea6c121271f034df5b543ed0cfaee57d2145bc0..e9811dc43b94d4eedce82b624ee2c53bbc1ab9bd 100644 (file)
@@ -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);
 } /* }}} */
-
index 571d065f59dd049282ad237669619dd4d4ad73fb..45f15791864b92cea3101047558adf0997c9a55c 100644 (file)
@@ -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
index a235fe8cb04c25a5ed46a2fdb4905a8b54830ccc..62b3a66febfe0a3b10dff70da583849af0500bca 100644 (file)
@@ -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;
                }
index a783bf44fcd6fac54ff225bfdd826cf6e6ca91d4..8d4bc530dba403a41b52d5f2abf880af5dd07bcc 100644 (file)
 
 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));
index e8db4e605c73f62e0bc96e107f5ee5a4337f47ec..5757d33ddad98ca783c1d1454378350573fca1b4 100644 (file)
 
 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;
+}
index 14905f65673b7123fa9ef1d2c33710ec127d0976..385ae20580422e185d9b46eceb3079f59f3e77ef 100644 (file)
@@ -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 */
index 76321a5042d90fdedbbabaa0bfced6d9851c3e0b..e2b4dd229cb89214d8dd61071eeea7de9203f670 100644 (file)
 
 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);
 
index 367926abd5f844c0b6966dda804e4c54f9848dd2..7bb84077303630fc8739f7ccf2ad912a8beb8938 100644 (file)
 
 /* {{{ 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();
+       }
 }
index 5909407e7dfa3547c5e6addcdea6b0e01a20d756..4247a44088739643e7e40424af7f4ba9918f559b 100644 (file)
 /* {{{ */
 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 */
index 54269a81931d63e421cdc06ebf5a9c5a21e30552..e7699a0c77fb0093ba05da06cf1d6b361850671f 100644 (file)
 
 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
 };
 
index 96ac713f232e1888ae89f684d2d484cb79b78b66..7238ad102fb34408ca37e0581170de6067f61d2b 100644 (file)
@@ -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) {
index 810e07c789c12964ebb971264b6d3372ba35db11..6ed74f53db9178d7fbcd1e19b8030d49e2817cfd 100644 (file)
@@ -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
index 4d3eb4cde68d2ff03bfcfc7668329b3dd9f28ea6..d12a304d129c7941a71164c0682e64eb6fea2cef 100644 (file)
@@ -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);
+       }
+}
index 9686d769480a7457fb2538e0b3a08e198b1f9fec..01aa8490fe55b9ee51a5ab0bb4be191c9084cf23 100644 (file)
@@ -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 */
index d00bcff77e5f4b5589ddc2e087a130032458962d..5c0079057cfede2e0937c8e3fca619f4ea6117c8 100644 (file)
@@ -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
 };