diff --git a/sapi/phpdbg/phpdbg.h b/sapi/phpdbg/phpdbg.h index 76630dc53c7d..88578168c6ea 100644 --- a/sapi/phpdbg/phpdbg.h +++ b/sapi/phpdbg/phpdbg.h @@ -254,6 +254,7 @@ ZEND_BEGIN_MODULE_GLOBALS(phpdbg) HashTable watch_recreation; /* watch elements pending recreation of their respective watchpoints */ HashTable watch_free; /* pointers to watch for being freed */ HashTable *watchlist_mem; /* triggered watchpoints */ + HashTable *original_watchlist_mem; /* the original allocation for watchlist_mem, used when watchlist_mem has changed temporarily */ HashTable *watchlist_mem_backup; /* triggered watchpoints backup table while iterating over it */ bool watchpoint_hit; /* a watchpoint was hit */ void (*original_free_function)(void *); /* the original AG(mm_heap)->_free function */ diff --git a/sapi/phpdbg/phpdbg_prompt.c b/sapi/phpdbg/phpdbg_prompt.c index 994ac829b0a5..887064cf2e70 100644 --- a/sapi/phpdbg/phpdbg_prompt.c +++ b/sapi/phpdbg/phpdbg_prompt.c @@ -1551,6 +1551,8 @@ int phpdbg_interactive(bool allow_async_unsafe, char *input) /* {{{ */ ret = phpdbg_stack_execute(&stack, allow_async_unsafe); } zend_catch { phpdbg_stack_free(&stack); + phpdbg_destroy_input(&input); + /* TODO: should use proper unwinding instead of bailing out */ zend_bailout(); } zend_end_try(); diff --git a/sapi/phpdbg/phpdbg_watch.c b/sapi/phpdbg/phpdbg_watch.c index fef29a829383..54db3edbc9c4 100644 --- a/sapi/phpdbg/phpdbg_watch.c +++ b/sapi/phpdbg/phpdbg_watch.c @@ -516,7 +516,9 @@ phpdbg_watch_element *phpdbg_add_watch_element(phpdbg_watchpoint_t *watch, phpdb phpdbg_watch_element *old_element; watch = res->ptr; if ((old_element = zend_hash_find_ptr(&watch->elements, element->str))) { - phpdbg_free_watch_element(element); + if (element != old_element) { + phpdbg_free_watch_element(element); + } return old_element; } } @@ -1471,6 +1473,7 @@ void phpdbg_setup_watchpoints(void) { /* put these on a separate page, to avoid conflicts with other memory */ PHPDBG_G(watchlist_mem) = malloc(phpdbg_pagesize > sizeof(HashTable) ? phpdbg_pagesize : sizeof(HashTable)); + PHPDBG_G(original_watchlist_mem) = PHPDBG_G(watchlist_mem); zend_hash_init(PHPDBG_G(watchlist_mem), phpdbg_pagesize / (sizeof(Bucket) + sizeof(uint32_t)), NULL, NULL, 1); PHPDBG_G(watchlist_mem_backup) = malloc(phpdbg_pagesize > sizeof(HashTable) ? phpdbg_pagesize : sizeof(HashTable)); zend_hash_init(PHPDBG_G(watchlist_mem_backup), phpdbg_pagesize / (sizeof(Bucket) + sizeof(uint32_t)), NULL, NULL, 1); @@ -1517,8 +1520,8 @@ void phpdbg_destroy_watchpoints(void) { zend_hash_destroy(&PHPDBG_G(watch_recreation)); zend_hash_destroy(&PHPDBG_G(watch_free)); zend_hash_destroy(&PHPDBG_G(watch_collisions)); - zend_hash_destroy(PHPDBG_G(watchlist_mem)); - free(PHPDBG_G(watchlist_mem)); + zend_hash_destroy(PHPDBG_G(original_watchlist_mem)); + free(PHPDBG_G(original_watchlist_mem)); zend_hash_destroy(PHPDBG_G(watchlist_mem_backup)); free(PHPDBG_G(watchlist_mem_backup)); } diff --git a/sapi/phpdbg/tests/gh15210_001.phpt b/sapi/phpdbg/tests/gh15210_001.phpt new file mode 100644 index 000000000000..c7b4756f7520 --- /dev/null +++ b/sapi/phpdbg/tests/gh15210_001.phpt @@ -0,0 +1,30 @@ +--TEST-- +GH-15210 use after free after continue +--PHPDBG-- +b 4 +r +w $a[0] +c +q +--FILE-- + +--EXPECTF-- +[Successful compilation of %s] +prompt> [Breakpoint #0 added at %s:%d] +prompt> [Breakpoint #0 at %s:%d, hits: 1] +>00004: $a[0] = 1; + 00005: ?> + 00006: +prompt> [Added watchpoint #0 for $a[0]] +prompt> [Breaking on watchpoint $a[0]] +Old value: [Breaking on watchpoint $a[0]] +Old value: 0 +New value: 1 +>00002: header_register_callback(function() { echo "sent";}); + 00003: $a = [0]; + 00004: $a[0] = 1; +prompt> [$a[0] has been removed, removing watchpoint] diff --git a/sapi/phpdbg/tests/gh15210_002.phpt b/sapi/phpdbg/tests/gh15210_002.phpt new file mode 100644 index 000000000000..0ba320eb5e15 --- /dev/null +++ b/sapi/phpdbg/tests/gh15210_002.phpt @@ -0,0 +1,36 @@ +--TEST-- +GH-15210 use after free after continue +--PHPDBG-- +b 4 +r +w $a[0] +c +c +q +--FILE-- + +--EXPECTF-- +[Successful compilation of %s] +prompt> [Breakpoint #0 added at %s:%d] +prompt> [Breakpoint #0 at %s:%d, hits: 1] +>00004: $a[0] = 1; + 00005: ?> + 00006: +prompt> [Added watchpoint #0 for $a[0]] +prompt> [Breaking on watchpoint $a[0]] +Old value: [Breaking on watchpoint $a[0]] +Old value: 0 +New value: 1 +>00002: header_register_callback(function() { echo "sent";}); + 00003: $a = [0]; + 00004: $a[0] = 1; +prompt> sent0 +New value: 1 + +[$a[0] has been removed, removing watchpoint] +[Script ended normally] +prompt>