112# include <sys/mman.h>
115#ifdef HAVE_USERFAULTFD_WRITEFAULT
117# include <linux/userfaultfd.h>
118# include <sys/ioctl.h>
119# include <sys/syscall.h>
131#define HT_FROM_ZVP(zvp) (Z_TYPE_P(zvp) == IS_OBJECT ? Z_OBJPROP_P(zvp) : Z_TYPE_P(zvp) == IS_ARRAY ? Z_ARRVAL_P(zvp) : NULL)
133#define HT_WATCH_OFFSET (sizeof(zend_refcounted *) + sizeof(uint32_t))
134#define HT_PTR_HT(ptr) ((HashTable *) (((char *) (ptr)) - HT_WATCH_OFFSET))
135#define HT_WATCH_HT(watch) HT_PTR_HT((watch)->addr.ptr)
147 return memcmp(oldPtr, newPtr,
sizeof(
zend_value) +
sizeof(uint32_t) ) != 0;
149 return zend_hash_num_elements(
HT_PTR_HT(oldPtr)) != zend_hash_num_elements(
HT_PTR_HT(newPtr));
151 return memcmp(oldPtr, newPtr,
sizeof(uint32_t) ) != 0;
173 phpdbg_writeln(
"Old value inaccessible or destroyed (was indirect)");
190 elementDiff = zend_hash_num_elements(
HT_PTR_HT(oldPtr)) - zend_hash_num_elements(
HT_PTR_HT(newPtr));
191 if (elementDiff > 0) {
192 phpdbg_writeln(
"%d elements were removed from the array", (
int) elementDiff);
193 }
else if (elementDiff < 0) {
194 phpdbg_writeln(
"%d elements were added to the array", (
int) -elementDiff);
230 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) {
239 void *page_addr = phpdbg_get_page_boundary(watch->
addr.
ptr);
240 size_t size = phpdbg_get_total_page_size(watch->
addr.
ptr, watch->
size);
241#ifdef HAVE_USERFAULTFD_WRITEFAULT
243 struct uffdio_range range = {
244 .start = (__u64)(uintptr_t) page_addr,
248 struct uffdio_register reg = {
249 .mode = UFFDIO_REGISTER_MODE_WP,
252 struct uffdio_writeprotect protect = {
253 .mode = UFFDIO_WRITEPROTECT_MODE_WP,
256 ioctl(
PHPDBG_G(watch_userfaultfd), UFFDIO_REGISTER, ®);
257 ioctl(
PHPDBG_G(watch_userfaultfd), UFFDIO_WRITEPROTECT, &protect);
259 struct uffdio_register reg = {
260 .mode = UFFDIO_REGISTER_MODE_WP,
263 ioctl(
PHPDBG_G(watch_userfaultfd), UFFDIO_UNREGISTER, ®);
274 phpdbg_change_watchpoint_access(watch,
PROT_READ);
288 void *page = phpdbg_get_page_boundary(
309#ifdef HAVE_USERFAULTFD_WRITEFAULT
310# if defined(__GNUC__) && !defined(__clang__)
313void *phpdbg_watchpoint_userfaultfd_thread(
void *phpdbg_globals) {
314 pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS,
NULL);
315 zend_phpdbg_globals *globals = (zend_phpdbg_globals *) phpdbg_globals;
317 struct uffd_msg fault_msg = {0};
318 while (read(globals->watch_userfaultfd, &fault_msg,
sizeof(fault_msg)) ==
sizeof(fault_msg)) {
319 void *page = phpdbg_get_page_boundary((
char *)(uintptr_t) fault_msg.arg.pagefault.address);
321 struct uffdio_writeprotect unprotect = {
324 .start = (__u64)(uintptr_t) page,
325 .len = phpdbg_pagesize
328 ioctl(globals->watch_userfaultfd, UFFDIO_WRITEPROTECT, &unprotect);
373 switch (watch->
type) {
399 if (zend_hash_num_elements(&coll->
parents) == 0) {
400 phpdbg_remove_watchpoint_btree(&coll->
ref);
401 phpdbg_deactivate_watchpoint(&coll->
ref);
406 phpdbg_remove_watchpoint_btree(&coll->
reference);
407 phpdbg_deactivate_watchpoint(&coll->
reference);
441 phpdbg_store_watchpoint_btree(&coll->
ref);
442 phpdbg_activate_watchpoint(&coll->
ref);
449 phpdbg_store_watchpoint_btree(&coll->
reference);
450 phpdbg_activate_watchpoint(&coll->
reference);
456 phpdbg_store_watchpoint_btree(&coll->
reference);
457 phpdbg_activate_watchpoint(&coll->
reference);
484 phpdbg_store_watchpoint_btree(&coll->
ref);
485 phpdbg_activate_watchpoint(&coll->
ref);
492 }
else if (watch->
ref) {
512 phpdbg_store_watchpoint_btree(watch);
516 phpdbg_activate_watchpoint(watch);
521 if ((old_element = zend_hash_find_ptr(&watch->
elements, element->
str))) {
522 if (element != old_element) {
529 element->
watch = watch;
530 zend_hash_add_ptr(&watch->
elements, element->
str, element);
579 child =
emalloc(
sizeof(*child));
588 }
else { str = zend_string_copy(str); }
608 if (element->
child) {
614 if (element->
child) {
621 if (element->
child) {
622 child = element->
child;
624 child =
emalloc(
sizeof(*child));
630 element->
child = child;
659 phpdbg_store_watchpoint_btree(&hti->
hash_watch);
676 if (zend_hash_num_elements(&hti->
watches) == 1) {
679 phpdbg_remove_watchpoint_btree(&hti->
hash_watch);
680 phpdbg_deactivate_watchpoint(&hti->
hash_watch);
702 if (child == element) {
705 child = child->
child;
764 child = child->
child;
806 zend_string_release(element->
str);
815 if (element->
child) {
836 parent = parent->parent;
842 child = child->child;
875 while (child->
child != until) {
876 child = child->
child;
900 parent = parent->parent;
906 child = child->child;
916 child = child->
child;
952 phpdbg_remove_watchpoint_btree(watch);
953 phpdbg_deactivate_watchpoint(watch);
973 if (element->
watch) {
976 if (zend_hash_num_elements(elements) == 0) {
990 zend_string_release(
name);
1029 str = zend_string_copy(str);
1032 zend_string_release(str);
1041 zend_string_release(str);
1053 switch (watch->
type) {
1061 comparePtr = &watch->
backup;
1088 phpdbg_remove_watchpoint_btree(watch);
1089 phpdbg_deactivate_watchpoint(watch);
1091 phpdbg_store_watchpoint_btree(watch);
1092 phpdbg_activate_watchpoint(watch);
1109 zend_string_release(
name);
1135 if ((
char *) page < (
char *) watch->
addr.
ptr + watch->
size) {
1136#ifdef HAVE_USERFAULTFD_WRITEFAULT
1138 struct uffdio_writeprotect protect = {
1139 .mode = UFFDIO_WRITEPROTECT_MODE_WP,
1141 .start = (__u64) page,
1142 .
len = phpdbg_pagesize
1145 ioctl(
PHPDBG_G(watch_userfaultfd), UFFDIO_WRITEPROTECT, &protect);
1182 if ((
char *) page < (
char *) watch->
addr.
ptr + watch->
size) {
1272 zend_string_release(element->
str);
1276 element->
child =
new;
1279 new->str = zend_string_copy(str);
1280 new->parent = element;
1315 if (element->
child) {
1316 element = element->
child;
1347 element->
str = zend_string_copy(info->
str);
1369 if (
scope &&
len >= 5 && !memcmp(
"$this", input, 5)) {
1373 if (
callback == phpdbg_create_array_watchpoint) {
1376 info.
str = zend_string_init(input,
len, 0);
1381 zend_string_release(info.
str);
1387 zend_string_release(info.
str);
1394 switch (param->type) {
1400 phpdbg_error(
"Nothing was deleted, no corresponding watchpoint found");
1415 return phpdbg_watchpoint_parse_symtables(input,
len, phpdbg_create_simple_watchpoint);
1424 switch (param->type) {
1426 phpdbg_watchpoint_parse_symtables(param->str, param->len, phpdbg_create_recursive_watchpoint);
1441 switch (param->type) {
1443 phpdbg_watchpoint_parse_symtables(param->str, param->len, phpdbg_create_array_watchpoint);
1454#if defined(_SC_PAGE_SIZE)
1455 phpdbg_pagesize = sysconf(_SC_PAGE_SIZE);
1456#elif defined(_SC_PAGESIZE)
1457 phpdbg_pagesize = sysconf(_SC_PAGESIZE);
1458#elif defined(_SC_NUTC_OS_PAGESIZE)
1459 phpdbg_pagesize = sysconf(_SC_NUTC_OS_PAGESIZE);
1461 phpdbg_pagesize = 4096;
1481#ifdef HAVE_USERFAULTFD_WRITEFAULT
1483#ifdef UFFD_USER_MODE_ONLY
1487 flags |= UFFD_USER_MODE_ONLY;
1489 PHPDBG_G(watch_userfaultfd) = syscall(SYS_userfaultfd,
flags);
1490 if (
PHPDBG_G(watch_userfaultfd) < 0) {
1493 struct uffdio_api userfaultfd_features = {0};
1494 userfaultfd_features.api = UFFD_API;
1495 userfaultfd_features.features = UFFD_FEATURE_PAGEFAULT_FLAG_WP;
1496 ioctl(
PHPDBG_G(watch_userfaultfd), UFFDIO_API, &userfaultfd_features);
1497 if (userfaultfd_features.features & UFFD_FEATURE_PAGEFAULT_FLAG_WP) {
1517#ifdef HAVE_USERFAULTFD_WRITEFAULT
1519 pthread_cancel(
PHPDBG_G(watch_userfault_thread));
1540 phpdbg_deactivate_watchpoint(
res->ptr);
prev(array|object &$array)
unsigned const char * pos
unsigned char key[REFLECTION_KEY_LEN]
HashTable * watchlist_mem_backup
HashTable watch_collisions
phpdbg_watch_element * watch_tmp
HashTable * watchlist_mem
#define PHPDBG_SHOW_REFCOUNTS
HashTable watch_recreation
phpdbg_btree watchpoint_tree
phpdbg_btree watch_HashTables
HashTable * original_watchlist_mem
void(* original_free_function)(void *ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC)
phpdbg_btree_result * phpdbg_btree_next(phpdbg_btree_position *pos)
void phpdbg_btree_init(phpdbg_btree *tree, zend_ulong depth)
phpdbg_btree_result * phpdbg_btree_find(phpdbg_btree *tree, zend_ulong idx)
int phpdbg_btree_delete(phpdbg_btree *tree, zend_ulong idx)
phpdbg_btree_result * phpdbg_btree_find_closest(phpdbg_btree *tree, zend_ulong idx)
phpdbg_btree_position phpdbg_btree_find_between(phpdbg_btree *tree, zend_ulong lower_idx, zend_ulong higher_idx)
#define phpdbg_btree_insert(tree, idx, ptr)
#define PHPDBG_END_COMMAND
#define phpdbg_default_switch_case()
struct _phpdbg_command_t phpdbg_command_t
#define PHPDBG_COMMAND_D_EX(name, tip, alias, handler, children, args, flags)
#define phpdbg_error(strfmt,...)
#define phpdbg_out(fmt,...)
#define phpdbg_notice(strfmt,...)
#define phpdbg_writeln(strfmt,...)
const phpdbg_command_t phpdbg_prompt_commands[]
int phpdbg_is_auto_global(char *name, int len)
PHPDBG_API int phpdbg_parse_variable_with_arg(char *input, size_t len, HashTable *parent, size_t i, phpdbg_parse_var_with_arg_func callback, phpdbg_parse_var_with_arg_func step_cb, bool silent, void *arg)
char * phpdbg_get_property_key(char *key)
int phpdbg_rebuild_symtable(void)
int(* phpdbg_parse_var_with_arg_func)(char *name, size_t len, char *keyname, size_t keylen, HashTable *parent, zval *zv, void *arg)
PHPDBG_API int phpdbg_watchpoint_parse_input(char *input, size_t len, HashTable *parent, size_t i, phpdbg_watch_parse_struct *info, bool silent)
void phpdbg_dequeue_elements_for_recreation(void)
zend_string * phpdbg_watchpoint_change_collision_name(phpdbg_watchpoint_t *watch)
bool phpdbg_try_re_adding_watch_element(zval *parent, phpdbg_watch_element *element)
void phpdbg_watch_backup_data(phpdbg_watchpoint_t *watch)
void phpdbg_check_watchpoint(phpdbg_watchpoint_t *watch)
int phpdbg_watchpoint_segfault_handler(siginfo_t *info, void *context)
void phpdbg_remove_watch_element(phpdbg_watch_element *element)
phpdbg_watch_element * phpdbg_add_bucket_watch_element(Bucket *bucket, phpdbg_watch_element *element)
void phpdbg_purge_watchpoint_tree(void)
void phpdbg_free_watch_element_tree(phpdbg_watch_element *element)
int phpdbg_print_changed_zvals(void)
int phpdbg_create_var_watchpoint(char *input, size_t len)
void phpdbg_update_watch_collision_elements(phpdbg_watchpoint_t *watch)
void phpdbg_list_watchpoints(void)
void phpdbg_print_watch_diff(phpdbg_watchtype type, zend_string *name, void *oldPtr, void *newPtr)
void phpdbg_set_zval_watchpoint(zval *zv, phpdbg_watchpoint_t *watch)
const phpdbg_command_t phpdbg_watch_commands[]
void phpdbg_destroy_watchpoints(void)
void phpdbg_remove_watchpoint(phpdbg_watchpoint_t *watch)
void phpdbg_delete_watch_collision(phpdbg_watchpoint_t *watch)
void phpdbg_recurse_watch_element(phpdbg_watch_element *element)
void phpdbg_watch_efree(void *ptr ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC)
void phpdbg_dissociate_watch_element(phpdbg_watch_element *element, phpdbg_watch_element *until)
void phpdbg_clean_watch_element(phpdbg_watch_element *element)
void phpdbg_queue_element_for_recreation(phpdbg_watch_element *element)
void phpdbg_backup_watch_element(phpdbg_watch_element *element)
#define HT_WATCH_HT(watch)
void phpdbg_remove_watch_element_recursively(phpdbg_watch_element *element)
bool phpdbg_check_watch_diff(phpdbg_watchtype type, void *oldPtr, void *newPtr)
void phpdbg_watch_parent_ht(phpdbg_watch_element *element)
phpdbg_watch_element * phpdbg_add_watch_element(phpdbg_watchpoint_t *watch, phpdbg_watch_element *element)
void phpdbg_set_addr_watchpoint(void *addr, size_t size, phpdbg_watchpoint_t *watch)
void phpdbg_free_watch_element(phpdbg_watch_element *element)
void phpdbg_reenable_memory_watches(void)
void phpdbg_set_bucket_watchpoint(Bucket *bucket, phpdbg_watchpoint_t *watch)
void phpdbg_setup_watchpoints(void)
void phpdbg_update_watch_element_watch(phpdbg_watch_element *element)
bool phpdbg_is_recursively_watched(void *ptr, phpdbg_watch_element *element)
phpdbg_watch_element * phpdbg_add_ht_watch_element(zval *zv, phpdbg_watch_element *element)
void phpdbg_update_watch_ref(phpdbg_watchpoint_t *watch)
void phpdbg_unwatch_parent_ht(phpdbg_watch_element *element)
void phpdbg_add_recursive_watch_from_ht(phpdbg_watch_element *element, zend_long idx, zend_string *str, zval *zv)
void phpdbg_automatic_dequeue_free(phpdbg_watch_element *element)
void phpdbg_set_ht_watchpoint(HashTable *ht, phpdbg_watchpoint_t *watch)
struct _phpdbg_watchpoint_t phpdbg_watchpoint_t
#define PHPDBG_WATCH_RECURSIVE_ROOT
#define PHPDBG_WATCH_SIMPLE
#define PHPDBG_WATCH_IMPLICIT
#define PHPDBG_WATCH_RECURSIVE
#define PHPDBG_WATCH(name)
struct _phpdbg_watch_collision phpdbg_watch_collision
#define PHPDBG_WATCH_OBJECT
#define PHPDBG_WATCH_ARRAY
struct _phpdbg_watch_element phpdbg_watch_element
int mprotect(void *addr, size_t size, int protection)
phpdbg_watchpoint_t reference
HashTable child_container
HashTable * parent_container
union _phpdbg_watch_element::@143130252033274031345066232267337246132126076271 backup
phpdbg_watchpoint_t * watch
zend_string * name_in_parent
struct _phpdbg_watch_element * parent
struct _phpdbg_watch_element * child
phpdbg_watch_collision * coll
union _phpdbg_watchpoint_t::@311026210203112077031145043110251145306324303024 addr
union _phpdbg_watchpoint_t::@353001364072153044355140237352142031313101134215 backup
phpdbg_watchpoint_t hash_watch
int(* callback)(zval *zv, phpdbg_watch_element *)
ZEND_API void zend_print_flat_zval_r(zval *expr)
ZEND_API zend_write_func_t zend_write
#define ZEND_MODULE_GLOBALS_BULK(module_name)
#define ZEND_EXTERN_MODULE_GLOBALS(module_name)
#define ecalloc(nmemb, size)
ZEND_API zend_class_entry * zend_get_executed_scope(void)
ZEND_API void ZEND_FASTCALL zend_hash_destroy(HashTable *ht)
ZEND_API zval *ZEND_FASTCALL zend_hash_index_add_empty_element(HashTable *ht, zend_ulong h)
ZEND_API void ZEND_FASTCALL zend_hash_clean(HashTable *ht)
ZEND_API zend_result ZEND_FASTCALL zend_hash_index_del(HashTable *ht, zend_ulong h)
ZEND_API zend_result ZEND_FASTCALL zend_hash_del(HashTable *ht, zend_string *key)
ZEND_API zval *ZEND_FASTCALL zend_hash_find(const HashTable *ht, zend_string *key)
ZEND_API zval *ZEND_FASTCALL zend_hash_add(HashTable *ht, zend_string *key, zval *pData)
ZEND_API zval *ZEND_FASTCALL zend_hash_index_find(const HashTable *ht, zend_ulong h)
#define zend_hash_init(ht, nSize, pHashFunction, pDestructor, persistent)
#define ZEND_HASH_MAP_FOREACH_PTR(ht, _ptr)
#define ZEND_HASH_REVERSE_FOREACH_KEY_VAL(ht, _h, _key, _val)
#define ZEND_HASH_FOREACH_PTR(ht, _ptr)
#define ZEND_HASH_FOREACH_KEY_VAL(ht, _h, _key, _val)
#define ZEND_HASH_MAP_FOREACH_NUM_KEY(ht, _h)
#define ZEND_HASH_FOREACH_END()
struct _zend_string zend_string
ZEND_API zend_string *ZEND_FASTCALL zend_long_to_str(zend_long num)
#define ZEND_FILE_LINE_DC
#define ZEND_FILE_LINE_ORIG_RELAY_CC
#define XtOffsetOf(s_type, field)
#define ZEND_UNREACHABLE()
#define ZEND_FILE_LINE_RELAY_CC
#define ZEND_FILE_LINE_ORIG_DC
struct _zend_class_entry zend_class_entry
#define Z_ISREF_P(zval_p)
#define Z_REFVAL_P(zval_p)
#define HT_HASH_SIZE(nTableMask)
#define Z_REFCOUNTED_P(zval_p)
struct _zend_array HashTable
union _zend_value zend_value
#define Z_COUNTED_P(zval_p)
#define GC_MAKE_PERSISTENT_LOCAL(p)
#define Z_STRLEN_P(zval_p)
#define HT_GET_DATA_ADDR(ht)
struct _zend_refcounted zend_refcounted
#define Z_INDIRECT_P(zval_p)