VOID SigIoWatcherThread(VOID *p) { zend_uchar sig; struct win32_sigio_watcher_data *swd = (struct win32_sigio_watcher_data *)p; #ifdef ZTS void ***tsrm_ls = swd->tsrm_ls; #endif top: (void)phpdbg_consume_bytes(swd->fd, &sig, 1, -1 TSRMLS_CC); if (3 == sig) { /* XXX completely not sure it is done right here */ if (PHPDBG_G(flags) & PHPDBG_IS_INTERACTIVE) { if (raise(sig)) { goto top; } } if (PHPDBG_G(flags) & PHPDBG_IS_SIGNALED) { phpdbg_set_sigsafe_mem(&sig TSRMLS_CC); zend_try { phpdbg_force_interruption(TSRMLS_C); } zend_end_try(); phpdbg_clear_sigsafe_mem(TSRMLS_C); goto end; } if (!(PHPDBG_G(flags) & PHPDBG_IS_INTERACTIVE)) { PHPDBG_G(flags) |= PHPDBG_IS_SIGNALED; } end: /* XXX set signaled flag to the caller thread, question is - whether it's needed */ ExitThread(sig); } else {
static int phpdbg_output_pager(int sock, const char *ptr, int len) { int count = 0, bytes = 0; const char *p = ptr, *endp = ptr + len; while ((p = memchr(p, '\n', endp - p))) { count++; p++; if (count % PHPDBG_G(lines) == 0) { bytes += write(sock, ptr + bytes, (p - ptr) - bytes); if (memchr(p, '\n', endp - p)) { char buf[PHPDBG_MAX_CMD]; zend_quiet_write(sock, ZEND_STRL("\r---Type <return> to continue or q <return> to quit---")); phpdbg_consume_stdin_line(buf); if (*buf == 'q') { break; } write(sock, "\r", 1); } else break; } } if (bytes && count % PHPDBG_G(lines) != 0) { bytes += write(sock, ptr + bytes, len - bytes); } else if (!bytes) { bytes += write(sock, ptr, len); } return bytes; }
PHPDBG_API int phpdbg_mixed_write(int sock, const char *ptr, int len) { if (PHPDBG_G(flags) & PHPDBG_IS_REMOTE) { return phpdbg_send_bytes(sock, ptr, len); } if ((PHPDBG_G(flags) & PHPDBG_HAS_PAGINATION) && !(PHPDBG_G(flags) & PHPDBG_WRITE_XML) && PHPDBG_G(io)[PHPDBG_STDOUT].fd == sock && PHPDBG_G(lines) > 0) { return phpdbg_output_pager(sock, ptr, len); } return write(sock, ptr, len); }
int phpdbg_eol_global_update(char *name) { if (0 == memcmp(name, "CRLF", 4) || 0 == memcmp(name, "crlf", 4) || 0 == memcmp(name, "DOS", 3) || 0 == memcmp(name, "dos", 3)) { PHPDBG_G(eol) = PHPDBG_EOL_CRLF; } else if (0 == memcmp(name, "LF", 2) || 0 == memcmp(name, "lf", 2) || 0 == memcmp(name, "UNIX", 4) || 0 == memcmp(name, "unix", 4)) { PHPDBG_G(eol) = PHPDBG_EOL_LF; } else if (0 == memcmp(name, "CR", 2) || 0 == memcmp(name, "cr", 2) || 0 == memcmp(name, "MAC", 3) || 0 == memcmp(name, "mac", 3)) { PHPDBG_G(eol) = PHPDBG_EOL_CR; } else { return FAILURE; } return SUCCESS; }
PHPDBG_API int phpdbg_mixed_read(int sock, char *ptr, int len, int tmo) { if (PHPDBG_G(flags) & PHPDBG_IS_REMOTE) { return phpdbg_consume_bytes(sock, ptr, len, tmo); } return read(sock, ptr, len); }
PHPDBG_API int phpdbg_mixed_write(int sock, const char *ptr, int len) { if (PHPDBG_G(flags) & PHPDBG_IS_REMOTE) { return phpdbg_send_bytes(sock, ptr, len); } return write(sock, ptr, len); }
/* is easy to generalize ... but not needed for now */ PHPDBG_API int phpdbg_consume_stdin_line(char *buf) { int bytes = PHPDBG_G(input_buflen), len = 0; if (PHPDBG_G(input_buflen)) { memcpy(buf, PHPDBG_G(input_buffer), bytes); } PHPDBG_G(last_was_newline) = 1; do { int i; if (bytes <= 0) { continue; } for (i = len; i < len + bytes; i++) { if (buf[i] == '\x03') { if (i != len + bytes - 1) { memmove(buf + i, buf + i + 1, len + bytes - i - 1); } len--; i--; continue; } if (buf[i] == '\n') { PHPDBG_G(input_buflen) = len + bytes - 1 - i; if (PHPDBG_G(input_buflen)) { memcpy(PHPDBG_G(input_buffer), buf + i + 1, PHPDBG_G(input_buflen)); } if (i != PHPDBG_MAX_CMD - 1) { buf[i + 1] = 0; } return i; } } len += bytes; } while ((bytes = phpdbg_mixed_read(PHPDBG_G(io)[PHPDBG_STDIN].fd, buf + len, PHPDBG_MAX_CMD - len, -1)) > 0); if (bytes <= 0) { PHPDBG_G(flags) |= PHPDBG_IS_QUITTING | PHPDBG_IS_DISCONNECTED; zend_bailout(); return 0; } return bytes; }
static void* zend_mm_mem_alloc(zend_mm_storage *storage, size_t size, size_t alignment) { if (EXPECTED(size == PHPDBG_SIGSAFE_MEM_SIZE && !PHPDBG_G(sigsafe_mem).allocated)) { PHPDBG_G(sigsafe_mem).allocated = 1; return PHPDBG_G(sigsafe_mem).mem; } write(PHPDBG_G(io)[PHPDBG_STDERR].fd, ZEND_STRL("Tried to allocate more than " EXP_STR(PHPDBG_SIGSAFE_MEM_SIZE) " bytes from stack memory in signal handler ... bailing out of signal handler\n")); if (*EG(bailout)) { LONGJMP(*EG(bailout), FAILURE); } write(PHPDBG_G(io)[PHPDBG_STDERR].fd, ZEND_STRL("Bailed out without a bailout address in signal handler!\n")); return NULL; }
void phpdbg_init_lexer (phpdbg_param_t *stack, char *input) { PHPDBG_G(parser_stack) = stack; YYSETCONDITION(INITIAL); LEX(text) = YYCURSOR = (unsigned char *) input; LEX(len) = strlen(input); }
void phpdbg_print_opline_ex(zend_execute_data *execute_data, HashTable *vars, zend_bool ignore_flags) /* {{{ */ { /* force out a line while stepping so the user knows what is happening */ if (ignore_flags || (!(PHPDBG_G(flags) & PHPDBG_IS_QUIET) || (PHPDBG_G(flags) & PHPDBG_IS_STEPPING) || (PHPDBG_G(oplog)))) { zend_op *opline = (zend_op *) execute_data->opline; char *decode = phpdbg_decode_opline(&execute_data->func->op_array, opline, vars); if (ignore_flags || (!(PHPDBG_G(flags) & PHPDBG_IS_QUIET) || (PHPDBG_G(flags) & PHPDBG_IS_STEPPING))) { /* output line info */ phpdbg_notice("opline", "line=\"%u\" opline=\"%p\" opcode=\"%s\" op=\"%s\" file=\"%s\"", "L%-5u %16p %-30s %s %s", opline->lineno, opline, phpdbg_decode_opcode(opline->opcode), decode, execute_data->func->op_array.filename ? execute_data->func->op_array.filename->val : "unknown"); } if (!ignore_flags && PHPDBG_G(oplog)) { phpdbg_log_ex(fileno(PHPDBG_G(oplog)), "L%-5u %16p %-30s %s %s", opline->lineno, opline, phpdbg_decode_opcode(opline->opcode), decode, execute_data->func->op_array.filename ? execute_data->func->op_array.filename->val : "unknown"); } if (decode) { free(decode); } } } /* }}} */
/* {{{ proto void phpdbg_clear(void) instructs phpdbg to clear breakpoints */ static PHP_FUNCTION(phpdbg_clear) { zend_hash_clean(&PHPDBG_G(bp)[PHPDBG_BREAK_FILE]); zend_hash_clean(&PHPDBG_G(bp)[PHPDBG_BREAK_SYM]); zend_hash_clean(&PHPDBG_G(bp)[PHPDBG_BREAK_FUNCTION_OPLINE]); zend_hash_clean(&PHPDBG_G(bp)[PHPDBG_BREAK_METHOD_OPLINE]); zend_hash_clean(&PHPDBG_G(bp)[PHPDBG_BREAK_FILE_OPLINE]); zend_hash_clean(&PHPDBG_G(bp)[PHPDBG_BREAK_OPLINE]); zend_hash_clean(&PHPDBG_G(bp)[PHPDBG_BREAK_METHOD]); zend_hash_clean(&PHPDBG_G(bp)[PHPDBG_BREAK_COND]); } /* }}} */
static void phpdbg_remove_watch_collision(phpdbg_watchpoint_t *watch) { phpdbg_watch_collision *cur; if ((cur = zend_hash_index_find_ptr(&PHPDBG_G(watch_collisions), (zend_ulong) Z_COUNTED_P(watch->addr.zv)))) { if (cur->refs && !--cur->refs) { phpdbg_delete_watchpoints_recursive(watch); } zend_hash_del(&cur->watches, watch->str); zend_hash_del(&cur->implicit_watches, watch->str); if (zend_hash_num_elements(&cur->watches) > 0) { cur->watch = Z_PTR_P(zend_hash_get_current_data_ex(&cur->watches, NULL)); } else if (zend_hash_num_elements(&cur->implicit_watches) > 0) { cur->watch = Z_PTR_P(zend_hash_get_current_data_ex(&cur->implicit_watches, NULL)); } else { phpdbg_deactivate_watchpoint(cur->watch); phpdbg_remove_watchpoint(cur->watch); zend_hash_index_del(&PHPDBG_G(watch_collisions), (zend_ulong) Z_COUNTED_P(watch->addr.zv)); } } }
PHPDBG_API int phpdbg_mixed_read(int sock, char *ptr, int len, int tmo) { int ret; if (PHPDBG_G(flags) & PHPDBG_IS_REMOTE) { return phpdbg_consume_bytes(sock, ptr, len, tmo); } do { ret = read(sock, ptr, len); } while (ret == -1 && errno == EINTR); return ret; }
void phpdbg_set_sigsafe_mem(char *buffer) { phpdbg_signal_safe_mem *mem = &PHPDBG_G(sigsafe_mem); const zend_mm_handlers phpdbg_handlers = { zend_mm_mem_alloc, zend_mm_mem_free, NULL, NULL, }; mem->mem = buffer; mem->allocated = 0; mem->heap = zend_mm_startup_ex(&phpdbg_handlers, NULL, 0); mem->old_heap = zend_mm_set_heap(mem->heap); }
static phpdbg_watchpoint_t *phpdbg_check_for_watchpoint(void *addr) { phpdbg_watchpoint_t *watch; phpdbg_btree_result *result = phpdbg_btree_find_closest(&PHPDBG_G(watchpoint_tree), (zend_ulong)phpdbg_get_page_boundary(addr) + phpdbg_pagesize - 1); if (result == NULL) { return NULL; } watch = result->ptr; /* check if that addr is in a mprotect()'ed memory area */ if ((char *) phpdbg_get_page_boundary(watch->addr.ptr) > (char *) addr || (char *) phpdbg_get_page_boundary(watch->addr.ptr) + phpdbg_get_total_page_size(watch->addr.ptr, watch->size) < (char *) addr) { /* failure */ return NULL; } return watch; }
static phpdbg_watchpoint_t *phpdbg_get_refcount_watch(phpdbg_watchpoint_t *parent) { zend_refcounted *ref; phpdbg_btree_result *res; if (parent->type == WATCH_ON_HASHTABLE) { parent = parent->parent; if (!parent) { return NULL; } } ZEND_ASSERT(parent->type == WATCH_ON_ZVAL); ref = Z_COUNTED_P(parent->addr.zv); res = phpdbg_btree_find(&PHPDBG_G(watchpoint_tree), (zend_ulong) ref); if (res) { return res->ptr; } return NULL; }
/* {{{ proto mixed phpdbg_exec(string context) Attempt to set the execution context for phpdbg If the execution context was set previously it is returned If the execution context was not set previously boolean true is returned If the request to set the context fails, boolean false is returned, and an E_WARNING raised */ static PHP_FUNCTION(phpdbg_exec) { char *exec = NULL; int exec_len = 0; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &exec, &exec_len) == FAILURE) { return; } { struct stat sb; zend_bool result = 1; if (VCWD_STAT(exec, &sb) != FAILURE) { if (sb.st_mode & (S_IFREG|S_IFLNK)) { if (PHPDBG_G(exec)) { ZVAL_STRINGL(return_value, PHPDBG_G(exec), PHPDBG_G(exec_len), 1); efree(PHPDBG_G(exec)); result = 0; } PHPDBG_G(exec) = estrndup(exec, exec_len); PHPDBG_G(exec_len) = exec_len; if (result) ZVAL_BOOL(return_value, 1); } else { zend_error( E_WARNING, "Failed to set execution context (%s), not a regular file or symlink", exec); ZVAL_BOOL(return_value, 0); } } else { zend_error( E_WARNING, "Failed to set execution context (%s) the file does not exist", exec); ZVAL_BOOL(return_value, 0); } } } /* }}} */
static inline void phpdbg_remove_watchpoint(phpdbg_watchpoint_t *watch) { phpdbg_btree_delete(&PHPDBG_G(watchpoint_tree), (zend_ulong) watch->addr.ptr); }
PHPDBG_API int phpdbg_create_listenable_socket(const char *addr, unsigned short port, struct addrinfo *addr_res) { int sock = -1, rc; int reuse = 1; struct in6_addr serveraddr; struct addrinfo hints, *res = NULL; char port_buf[8]; int8_t any_addr = *addr == '*'; do { memset(&hints, 0, sizeof hints); if (any_addr) { hints.ai_flags = AI_PASSIVE; } else { hints.ai_flags = AI_NUMERICSERV; } hints.ai_family = AF_UNSPEC; hints.ai_socktype = SOCK_STREAM; rc = inet_pton(AF_INET, addr, &serveraddr); if (1 == rc) { hints.ai_family = AF_INET; if (!any_addr) { hints.ai_flags |= AI_NUMERICHOST; } } else { rc = inet_pton(AF_INET6, addr, &serveraddr); if (1 == rc) { hints.ai_family = AF_INET6; if (!any_addr) { hints.ai_flags |= AI_NUMERICHOST; } } else { /* XXX get host by name ??? */ } } snprintf(port_buf, 7, "%u", port); if (!any_addr) { rc = getaddrinfo(addr, port_buf, &hints, &res); } else { rc = getaddrinfo(NULL, port_buf, &hints, &res); } if (0 != rc) { #ifndef PHP_WIN32 if (rc == EAI_SYSTEM) { char buf[128]; int wrote; wrote = snprintf(buf, 128, "Could not translate address '%s'", addr); buf[wrote] = '\0'; write(PHPDBG_G(io)[PHPDBG_STDERR].fd, buf, strlen(buf)); return sock; } else { #endif char buf[256]; int wrote; wrote = snprintf(buf, 256, "Host '%s' not found. %s", addr, estrdup(gai_strerror(rc))); buf[wrote] = '\0'; write(PHPDBG_G(io)[PHPDBG_STDERR].fd, buf, strlen(buf)); return sock; #ifndef PHP_WIN32 } #endif return sock; } if((sock = socket(res->ai_family, res->ai_socktype, res->ai_protocol)) == -1) { char buf[128]; int wrote; wrote = sprintf(buf, "Unable to create socket"); buf[wrote] = '\0'; write(PHPDBG_G(io)[PHPDBG_STDERR].fd, buf, strlen(buf)); return sock; } if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char*) &reuse, sizeof(reuse)) == -1) { phpdbg_close_socket(sock); return sock; } } while (0); *addr_res = *res; return sock; }
void phpdbg_print_opline_ex(zend_execute_data *execute_data, zend_bool ignore_flags) /* {{{ */ { /* force out a line while stepping so the user knows what is happening */ if (ignore_flags || (!(PHPDBG_G(flags) & PHPDBG_IS_QUIET) || (PHPDBG_G(flags) & PHPDBG_IS_STEPPING) || (PHPDBG_G(oplog)))) { zend_op *opline = (zend_op *) execute_data->opline; char *decode = phpdbg_decode_opline(&execute_data->func->op_array, opline); if (ignore_flags || (!(PHPDBG_G(flags) & PHPDBG_IS_QUIET) || (PHPDBG_G(flags) & PHPDBG_IS_STEPPING))) { /* output line info */ phpdbg_notice("opline", "line=\"%u\" opline=\"%p\" op=\"%s\" file=\"%s\"", "L%-5u %16p %s %s", opline->lineno, opline, decode, execute_data->func->op_array.filename ? ZSTR_VAL(execute_data->func->op_array.filename) : "unknown"); } if (!ignore_flags && PHPDBG_G(oplog)) { phpdbg_log_ex(fileno(PHPDBG_G(oplog)), "L%-5u %16p %s %s\n", opline->lineno, opline, decode, execute_data->func->op_array.filename ? ZSTR_VAL(execute_data->func->op_array.filename) : "unknown"); } efree(decode); } if (PHPDBG_G(oplog_list)) { phpdbg_oplog_entry *cur = zend_arena_alloc(&PHPDBG_G(oplog_arena), sizeof(phpdbg_oplog_entry)); zend_op_array *op_array = &execute_data->func->op_array; cur->op = (zend_op *) execute_data->opline; cur->opcodes = op_array->opcodes; cur->filename = op_array->filename; cur->scope = op_array->scope; cur->function_name = op_array->function_name; cur->next = NULL; PHPDBG_G(oplog_cur)->next = cur; PHPDBG_G(oplog_cur) = cur; } } /* }}} */
PHPDBG_COMMAND_HELP_D(aliases, "show alias list", 'a', phpdbg_do_help_aliases), PHPDBG_COMMAND_HELP_D(options, "command line options", 0, NULL), PHPDBG_COMMAND_HELP_D(overview, "help overview", 0, NULL), PHPDBG_COMMAND_HELP_D(phpdbginit, "phpdbginit file format", 0, NULL), PHPDBG_COMMAND_HELP_D(syntax, "syntax overview", 0, NULL), PHPDBG_END_COMMAND }; /* }}} */ /* {{{ pretty_print. Formatting escapes and wrapping text in a string before printing it. */ void pretty_print(char *text TSRMLS_DC) { char *new, *p, *q; const char *prompt_escape = phpdbg_get_prompt(TSRMLS_C); unsigned int prompt_escape_len = strlen(prompt_escape); unsigned int prompt_len = strlen(PHPDBG_G(prompt)[0]); const char *bold_on_escape = PHPDBG_G(flags) & PHPDBG_IS_COLOURED ? "\033[1m" : ""; const char *bold_off_escape = PHPDBG_G(flags) & PHPDBG_IS_COLOURED ? "\033[0m" : ""; unsigned int bold_escape_len = strlen(bold_on_escape); unsigned int term_width = phpdbg_get_terminal_width(TSRMLS_C); unsigned int size = 0; int in_bold = 0; char *last_new_blank = NULL; /* position in new buffer of last blank char */ unsigned int last_blank_count = 0; /* printable char offset of last blank char */ unsigned int line_count = 0; /* number printable chars on current line */ /* First pass calculates a safe size for the pretty print version */
static inline void phpdbg_store_watchpoint(phpdbg_watchpoint_t *watch) { phpdbg_btree_insert(&PHPDBG_G(watchpoint_tree), (zend_ulong) watch->addr.ptr, watch); }
static PHP_RSHUTDOWN_FUNCTION(phpdbg) /* {{{ */ { zend_hash_destroy(&PHPDBG_G(bp)[PHPDBG_BREAK_FILE]); zend_hash_destroy(&PHPDBG_G(bp)[PHPDBG_BREAK_SYM]); zend_hash_destroy(&PHPDBG_G(bp)[PHPDBG_BREAK_FUNCTION_OPLINE]); zend_hash_destroy(&PHPDBG_G(bp)[PHPDBG_BREAK_METHOD_OPLINE]); zend_hash_destroy(&PHPDBG_G(bp)[PHPDBG_BREAK_FILE_OPLINE]); zend_hash_destroy(&PHPDBG_G(bp)[PHPDBG_BREAK_OPLINE]); zend_hash_destroy(&PHPDBG_G(bp)[PHPDBG_BREAK_OPCODE]); zend_hash_destroy(&PHPDBG_G(bp)[PHPDBG_BREAK_METHOD]); zend_hash_destroy(&PHPDBG_G(bp)[PHPDBG_BREAK_COND]); 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(watchpoints)); zend_llist_destroy(&PHPDBG_G(watchlist_mem)); if (PHPDBG_G(buffer)) { efree(PHPDBG_G(buffer)); PHPDBG_G(buffer) = NULL; } if (PHPDBG_G(exec)) { efree(PHPDBG_G(exec)); PHPDBG_G(exec) = NULL; } if (PHPDBG_G(prompt)[0]) { free(PHPDBG_G(prompt)[0]); } if (PHPDBG_G(prompt)[1]) { free(PHPDBG_G(prompt)[1]); } PHPDBG_G(prompt)[0] = NULL; PHPDBG_G(prompt)[1] = NULL; if (PHPDBG_G(oplog)) { fclose( PHPDBG_G(oplog)); PHPDBG_G(oplog) = NULL; } if (PHPDBG_G(ops)) { destroy_op_array(PHPDBG_G(ops) TSRMLS_CC); efree(PHPDBG_G(ops)); PHPDBG_G(ops) = NULL; } return SUCCESS; } /* }}} */
static PHP_RINIT_FUNCTION(phpdbg) /* {{{ */ { zend_hash_init(&PHPDBG_G(bp)[PHPDBG_BREAK_FILE], 8, NULL, php_phpdbg_destroy_bp_file, 0); zend_hash_init(&PHPDBG_G(bp)[PHPDBG_BREAK_SYM], 8, NULL, php_phpdbg_destroy_bp_symbol, 0); zend_hash_init(&PHPDBG_G(bp)[PHPDBG_BREAK_FUNCTION_OPLINE], 8, NULL, php_phpdbg_destroy_bp_methods, 0); zend_hash_init(&PHPDBG_G(bp)[PHPDBG_BREAK_METHOD_OPLINE], 8, NULL, php_phpdbg_destroy_bp_methods, 0); zend_hash_init(&PHPDBG_G(bp)[PHPDBG_BREAK_FILE_OPLINE], 8, NULL, php_phpdbg_destroy_bp_methods, 0); zend_hash_init(&PHPDBG_G(bp)[PHPDBG_BREAK_OPLINE], 8, NULL, NULL, 0); zend_hash_init(&PHPDBG_G(bp)[PHPDBG_BREAK_OPCODE], 8, NULL, php_phpdbg_destroy_bp_opcode, 0); zend_hash_init(&PHPDBG_G(bp)[PHPDBG_BREAK_METHOD], 8, NULL, php_phpdbg_destroy_bp_methods, 0); zend_hash_init(&PHPDBG_G(bp)[PHPDBG_BREAK_COND], 8, NULL, php_phpdbg_destroy_bp_condition, 0); zend_hash_init(&PHPDBG_G(bp)[PHPDBG_BREAK_MAP], 8, NULL, NULL, 0); zend_hash_init(&PHPDBG_G(seek), 8, NULL, NULL, 0); zend_hash_init(&PHPDBG_G(registered), 8, NULL, php_phpdbg_destroy_registered, 0); return SUCCESS; } /* }}} */
typedef struct { void *page; size_t size; char reenable_writing; /* data must be last element */ void *data; } phpdbg_watch_memdump; #define MEMDUMP_SIZE(size) (sizeof(phpdbg_watch_memdump) - sizeof(void *) + (size)) static phpdbg_watchpoint_t *phpdbg_check_for_watchpoint(void *addr TSRMLS_DC) { phpdbg_watchpoint_t *watch; phpdbg_btree_result *result = phpdbg_btree_find_closest(&PHPDBG_G(watchpoint_tree), (zend_ulong)phpdbg_get_page_boundary(addr) + phpdbg_pagesize - 1); if (result == NULL) { return NULL; } watch = result->ptr; /* check if that addr is in a mprotect()'ed memory area */ if ((char *)phpdbg_get_page_boundary(watch->addr.ptr) > (char *)addr || (char *)phpdbg_get_page_boundary(watch->addr.ptr) + phpdbg_get_total_page_size(watch->addr.ptr, watch->size) < (char *)addr) { /* failure */ return NULL; } return watch; }
static phpdbg_watchpoint_t *phpdbg_create_watchpoint(phpdbg_watchpoint_t *watch) { phpdbg_watchpoint_t *ret = watch; /* exclude references & refcounted */ if (!watch->parent || watch->parent->type != WATCH_ON_ZVAL || watch->type == WATCH_ON_HASHTABLE) { phpdbg_watchpoint_t *old_watch = zend_hash_find_ptr(&PHPDBG_G(watchpoints), watch->str); if (old_watch) { #define return_and_free_watch(x) { \ phpdbg_watchpoint_t *ref = phpdbg_get_refcount_watch(old_watch); \ if (ref) { \ phpdbg_add_watch_collision(ref); \ } \ if (watch != old_watch) { \ phpdbg_free_watch(watch); \ efree(watch); \ } \ return (x); \ } if (watch->flags & PHPDBG_WATCH_RECURSIVE) { if (old_watch->flags & PHPDBG_WATCH_RECURSIVE) { return_and_free_watch(NULL); } else { old_watch->flags &= ~(PHPDBG_WATCH_SIMPLE | PHPDBG_WATCH_IMPLICIT); old_watch->flags |= PHPDBG_WATCH_RECURSIVE; return_and_free_watch(old_watch); } } else { if (!(old_watch->flags & PHPDBG_WATCH_RECURSIVE)) { old_watch->flags |= watch->flags & (PHPDBG_WATCH_IMPLICIT | PHPDBG_WATCH_SIMPLE); } return_and_free_watch(NULL); } } else { if (watch->parent && watch->parent->type == WATCH_ON_HASHTABLE) { watch->parent->implicit_ht_count++; } zend_hash_add_ptr(&PHPDBG_G(watchpoints), watch->str, watch); } } phpdbg_store_watchpoint(watch); if (watch->parent && watch->parent->type == WATCH_ON_ZVAL && Z_REFCOUNTED_P(watch->parent->addr.zv)) { phpdbg_add_watch_collision(phpdbg_create_refcounted_watchpoint(watch, Z_COUNTED_P(watch->parent->addr.zv))); } if (watch->type == WATCH_ON_ZVAL) { if (watch->parent_container) { HashTable *ht_watches; phpdbg_btree_result *find; if (!(find = phpdbg_btree_find(&PHPDBG_G(watch_HashTables), (zend_ulong) watch->parent_container))) { phpdbg_watch_ht_info *hti = emalloc(sizeof(*hti)); hti->dtor = watch->parent_container->pDestructor; ht_watches = &hti->watches; zend_hash_init(ht_watches, 0, grrrrr, ZVAL_PTR_DTOR, 0); phpdbg_btree_insert(&PHPDBG_G(watch_HashTables), (zend_ulong) watch->parent_container, hti); watch->parent_container->pDestructor = (dtor_func_t) phpdbg_watch_HashTable_dtor; } else { ht_watches = &((phpdbg_watch_ht_info *) find->ptr)->watches; } zend_hash_add_ptr(ht_watches, watch->name_in_parent, watch); } if (Z_ISREF_P(watch->addr.zv)) { ret = phpdbg_create_reference_watch(watch); } } phpdbg_activate_watchpoint(watch); return ret; }
PHPDBG_API int phpdbg_consume_bytes(int sock, char *ptr, int len, int tmo) { int got_now, i = len, j; char *p = ptr; #ifndef PHP_WIN32 struct pollfd pfd; if (tmo < 0) goto recv_once; pfd.fd = sock; pfd.events = POLLIN; j = poll(&pfd, 1, tmo); if (j == 0) { #else struct fd_set readfds; struct timeval ttmo; if (tmo < 0) goto recv_once; FD_ZERO(&readfds); FD_SET(sock, &readfds); ttmo.tv_sec = 0; ttmo.tv_usec = tmo*1000; j = select(0, &readfds, NULL, NULL, &ttmo); if (j <= 0) { #endif return -1; } recv_once: while(i > 0) { if (tmo < 0) { /* There's something to read. Read what's available and proceed disregarding whether len could be exhausted or not.*/ int can_read = recv(sock, p, i, MSG_PEEK); #ifndef _WIN32 if (can_read == -1 && errno == EINTR) { continue; } #endif i = can_read; } #ifdef _WIN32 got_now = recv(sock, p, i, 0); #else do { got_now = recv(sock, p, i, 0); } while (got_now == -1 && errno == EINTR); #endif if (got_now == -1) { write(PHPDBG_G(io)[PHPDBG_STDERR].fd, ZEND_STRL("Read operation timed out!\n")); return -1; } i -= got_now; p += got_now; } return p - ptr; } PHPDBG_API int phpdbg_send_bytes(int sock, const char *ptr, int len) { int sent, i = len; const char *p = ptr; /* XXX poll/select needed here? */ while(i > 0) { sent = send(sock, p, i, 0); if (sent == -1) { return -1; } i -= sent; p += sent; } return len; }
zend_bool phpdbg_active_sigsafe_mem(void) { return !!PHPDBG_G(sigsafe_mem).mem; }
static inline int phpdbg_call_register(phpdbg_param_t *stack) /* {{{ */ { phpdbg_param_t *name = NULL; if (stack->type == STACK_PARAM) { char *lc_name; name = stack->next; if (!name || name->type != STR_PARAM) { return FAILURE; } lc_name = zend_str_tolower_dup(name->str, name->len); if (zend_hash_str_exists(&PHPDBG_G(registered), lc_name, name->len)) { zval fretval; zend_fcall_info fci; memset(&fci, 0, sizeof(zend_fcall_info)); ZVAL_STRINGL(&fci.function_name, lc_name, name->len); fci.size = sizeof(zend_fcall_info); fci.function_table = &PHPDBG_G(registered); fci.symbol_table = zend_rebuild_symbol_table(); fci.object = NULL; fci.retval = &fretval; fci.no_separation = 1; if (name->next) { zval params; phpdbg_param_t *next = name->next; array_init(¶ms); while (next) { char *buffered = NULL; switch (next->type) { case OP_PARAM: case COND_PARAM: case STR_PARAM: add_next_index_stringl(¶ms, next->str, next->len); break; case NUMERIC_PARAM: add_next_index_long(¶ms, next->num); break; case METHOD_PARAM: spprintf(&buffered, 0, "%s::%s", next->method.class, next->method.name); add_next_index_string(¶ms, buffered); break; case NUMERIC_METHOD_PARAM: spprintf(&buffered, 0, "%s::%s#%ld", next->method.class, next->method.name, next->num); add_next_index_string(¶ms, buffered); break; case NUMERIC_FUNCTION_PARAM: spprintf(&buffered, 0, "%s#%ld", next->str, next->num); add_next_index_string(¶ms, buffered); break; case FILE_PARAM: spprintf(&buffered, 0, "%s:%ld", next->file.name, next->file.line); add_next_index_string(¶ms, buffered); break; case NUMERIC_FILE_PARAM: spprintf(&buffered, 0, "%s:#%ld", next->file.name, next->file.line); add_next_index_string(¶ms, buffered); break; default: { /* not yet */ } } next = next->next; } zend_fcall_info_args(&fci, ¶ms); } else {
/* Must prevent duplicates ... if there are duplicates, replace new by old! */ static void phpdbg_add_watch_collision(phpdbg_watchpoint_t *watch) { phpdbg_watch_collision *cur; /* Check for either recursive or (simple and/or implicit) */ ZEND_ASSERT(((watch->flags & PHPDBG_WATCH_RECURSIVE) == 0) ^ ((watch->flags & (PHPDBG_WATCH_IMPLICIT | PHPDBG_WATCH_SIMPLE)) == 0)); if ((cur = zend_hash_index_find_ptr(&PHPDBG_G(watch_collisions), (zend_ulong) watch->addr.ref))) { phpdbg_watchpoint_t *old; int flags = 0; if ((old = zend_hash_find_ptr(&cur->watches, watch->str)) || (old = zend_hash_find_ptr(&cur->implicit_watches, watch->str))) { if (((old->flags ^ watch->flags) & (PHPDBG_WATCH_NORMAL|PHPDBG_WATCH_IMPLICIT)) == 0) { return; /* there was no change ... */ } flags = old->flags; if (flags & PHPDBG_WATCH_RECURSIVE) { if (!(watch->flags & PHPDBG_WATCH_RECURSIVE) && !--cur->refs) { phpdbg_delete_watchpoints_recursive(watch); } } if (flags & PHPDBG_WATCH_NORMAL) { zend_hash_del(&cur->watches, watch->str); if (zend_hash_num_elements(&cur->watches) > 0) { cur->watch = Z_PTR_P(zend_hash_get_current_data_ex(&cur->watches, NULL)); } else { cur->watch = Z_PTR_P(zend_hash_get_current_data_ex(&cur->implicit_watches, NULL)); } } if (flags & PHPDBG_WATCH_IMPLICIT) { zend_hash_del(&cur->implicit_watches, watch->str); } old->flags = watch->flags; phpdbg_free_watch(watch); efree(watch); watch = old; } if (watch->flags & PHPDBG_WATCH_RECURSIVE) { if (!(flags & PHPDBG_WATCH_RECURSIVE) && !cur->refs++) { phpdbg_create_recursive_zval_watch(watch->parent); } } } else { phpdbg_watch_collision coll; coll.refs = (watch->flags & PHPDBG_WATCH_RECURSIVE) != 0; coll.watch = watch; zend_hash_init(&coll.watches, 8, arghs, NULL, 0); zend_hash_init(&coll.implicit_watches, 8, ..., NULL, 0); cur = zend_hash_index_add_mem(&PHPDBG_G(watch_collisions), (zend_ulong) watch->addr.ref, &coll, sizeof(phpdbg_watch_collision)); phpdbg_store_watchpoint(cur->watch); phpdbg_activate_watchpoint(cur->watch); if (coll.refs) { phpdbg_create_recursive_zval_watch(watch->parent); } } if (watch->flags & PHPDBG_WATCH_NORMAL) { cur->watch = watch; zend_hash_add_ptr(&cur->watches, watch->str, watch->parent); } if (watch->flags & PHPDBG_WATCH_IMPLICIT) { zend_hash_add_ptr(&cur->implicit_watches, watch->str, watch->parent); } }