From 088148d8557d1c8afd990549e20480be70e9adfe Mon Sep 17 00:00:00 2001 From: Akihito Koriyama Date: Fri, 28 Jun 2024 03:45:27 +0900 Subject: [PATCH] (!) Add custom memory management and string handling to rayaop This commit introduces custom memory management functions to the rayaop extension, replacing calls to standard PHP functions with custom ones. It also includes a customized string handling mechanism using the rayaop_string_* series of functions, which enable direct allocation and deallocation while integrating custom debug prints for tracking. Additionally, a memory validation function and a refcount tracking function have been implemented to enhance memory management reliability and transparency. --- src/rayaop.c | 178 ++++++++++++++++++++++++++++++++++----------------- 1 file changed, 118 insertions(+), 60 deletions(-) diff --git a/src/rayaop.c b/src/rayaop.c index 491cd8a..2ab2905 100644 --- a/src/rayaop.c +++ b/src/rayaop.c @@ -14,6 +14,63 @@ #define RAYAOP_DEBUG +#ifdef RAYAOP_DEBUG +#define RAYAOP_DEBUG_PRINT(fmt, ...) php_printf("RAYAOP DEBUG: " fmt "\n", ##__VA_ARGS__) +#else +#define RAYAOP_DEBUG_PRINT(fmt, ...) +#endif + +#include +#include + +// Custom memory management functions +static void* rayaop_malloc(size_t size) { + void* ptr = malloc(size); + RAYAOP_DEBUG_PRINT("rayaop_malloc: Allocated %zu bytes at %p", size, ptr); + return ptr; +} + +static void rayaop_free(void* ptr) { + RAYAOP_DEBUG_PRINT("rayaop_free: Freeing memory at %p", ptr); + free(ptr); +} + +static void* rayaop_realloc(void* ptr, size_t size) { + void* new_ptr = realloc(ptr, size); + RAYAOP_DEBUG_PRINT("rayaop_realloc: Reallocated from %p to %p, new size %zu", ptr, new_ptr, size); + return new_ptr; +} + +// Custom zend_string management +static zend_string* rayaop_string_init(const char* str, size_t len, int persistent) { + size_t total_size = sizeof(zend_string) + len + 1; + zend_string* zstr = (zend_string*)rayaop_malloc(total_size); + if (!zstr) { + return NULL; + } + GC_SET_REFCOUNT(zstr, 1); + GC_TYPE_INFO(zstr) = IS_STRING; + if (persistent) { + GC_ADD_FLAGS(zstr, IS_STR_PERSISTENT); + } + zstr->h = 0; + zstr->len = len; + memcpy(ZSTR_VAL(zstr), str, len); + ZSTR_VAL(zstr)[len] = '\0'; + return zstr; +} + +static void rayaop_string_release(zend_string* str) { + if (!str) { + return; + } + RAYAOP_DEBUG_PRINT("rayaop_string_release: Releasing string '%s' at %p", ZSTR_VAL(str), str); + if (GC_DELREF(str) == 0) { + RAYAOP_DEBUG_PRINT("rayaop_string_release: Freeing string memory"); + rayaop_free(str); + } +} + typedef struct _intercept_info { zend_string *class_name; zend_string *method_name; @@ -29,14 +86,6 @@ static THREAD_LOCAL zend_bool is_intercepting = 0; static zend_bool is_intercepting = 0; #endif -#ifdef RAYAOP_DEBUG -#define RAYAOP_DEBUG_PRINT(fmt, ...) php_printf("RAYAOP DEBUG: " fmt "\n", ##__VA_ARGS__) -#else -#define RAYAOP_DEBUG_PRINT(fmt, ...) -#endif - -#include - static void dump_memory(const void *ptr, size_t size) { const unsigned char *byte = ptr; @@ -72,11 +121,19 @@ static void dump_zval(zval *val) } } -static void check_heap_integrity(const char *location) { - RAYAOP_DEBUG_PRINT("Checking heap integrity at %s", location); - // ここにヒープ整合性チェックのロジックを実装 - // 例: zend_mm_check_heapのような内部関数を使用(利用可能な場合) - // 注意: この関数の実際の実装はPHPのバージョンや利用可能なAPIに依存します +static void validate_memory(void* ptr, size_t size, const char* location) { + RAYAOP_DEBUG_PRINT("Validating memory at %p, size %zu, location: %s", ptr, size, location); + unsigned char* mem = (unsigned char*)ptr; + for (size_t i = 0; i < size; i++) { + if (mem[i] == 0xFE) { + RAYAOP_DEBUG_PRINT("Invalid memory content detected at offset %zu", i); + } + } +} + +static void track_refcount(zend_string* str, const char* location) { + RAYAOP_DEBUG_PRINT("Refcount for '%s' at %p: %d (Location: %s)", + ZSTR_VAL(str), str, GC_REFCOUNT(str), location); } static void safe_zend_string_release(zend_string **str_ptr) { @@ -88,13 +145,14 @@ static void safe_zend_string_release(zend_string **str_ptr) { dump_zend_string(*str_ptr); - check_heap_integrity("Before zend_string_release"); + validate_memory(*str_ptr, sizeof(zend_string) + ZSTR_LEN(*str_ptr), "Before zend_string_release"); + track_refcount(*str_ptr, "Before zend_string_release"); if (!ZSTR_IS_INTERNED(*str_ptr) && GC_REFCOUNT(*str_ptr) > 0) { RAYAOP_DEBUG_PRINT("Memory content before release:"); dump_memory(*str_ptr, sizeof(zend_string) + ZSTR_LEN(*str_ptr) + 1); - zend_string_release(*str_ptr); + rayaop_string_release(*str_ptr); *str_ptr = NULL; RAYAOP_DEBUG_PRINT("zend_string released and nullified"); } else if (ZSTR_IS_INTERNED(*str_ptr)) { @@ -105,7 +163,7 @@ static void safe_zend_string_release(zend_string **str_ptr) { *str_ptr = NULL; } - check_heap_integrity("After zend_string_release"); + validate_memory(*str_ptr, sizeof(zend_string) + ZSTR_LEN(*str_ptr), "After zend_string_release"); } else { RAYAOP_DEBUG_PRINT("Attempted to release NULL zend_string"); } @@ -114,7 +172,7 @@ static void safe_zend_string_release(zend_string **str_ptr) { static void free_intercept_info(zval *zv) { RAYAOP_DEBUG_PRINT("Entering free_intercept_info"); - check_heap_integrity("Start of free_intercept_info"); + validate_memory(Z_PTR_P(zv), sizeof(intercept_info), "Start of free_intercept_info"); intercept_info *info = Z_PTR_P(zv); if (info) { @@ -127,33 +185,33 @@ static void free_intercept_info(zval *zv) dump_zend_string(info->class_name); RAYAOP_DEBUG_PRINT("Memory around class_name:"); dump_memory((char*)info->class_name - 16, sizeof(zend_string) + ZSTR_LEN(info->class_name) + 32); - RAYAOP_DEBUG_PRINT("class_name refcount: %d", GC_REFCOUNT(info->class_name)); + track_refcount(info->class_name, "Before releasing class_name"); } - check_heap_integrity("Before releasing class_name"); + validate_memory(info->class_name, sizeof(zend_string) + (info->class_name ? ZSTR_LEN(info->class_name) : 0), "Before releasing class_name"); safe_zend_string_release(&info->class_name); - check_heap_integrity("After releasing class_name"); + validate_memory(info, sizeof(intercept_info), "After releasing class_name"); if (info->method_name) { RAYAOP_DEBUG_PRINT("method_name before release:"); dump_zend_string(info->method_name); RAYAOP_DEBUG_PRINT("Memory around method_name:"); dump_memory((char*)info->method_name - 16, sizeof(zend_string) + ZSTR_LEN(info->method_name) + 32); - RAYAOP_DEBUG_PRINT("method_name refcount: %d", GC_REFCOUNT(info->method_name)); + track_refcount(info->method_name, "Before releasing method_name"); } - check_heap_integrity("Before releasing method_name"); + validate_memory(info->method_name, sizeof(zend_string) + (info->method_name ? ZSTR_LEN(info->method_name) : 0), "Before releasing method_name"); safe_zend_string_release(&info->method_name); - check_heap_integrity("After releasing method_name"); + validate_memory(info, sizeof(intercept_info), "After releasing method_name"); RAYAOP_DEBUG_PRINT("Releasing handler"); zval_ptr_dtor(&info->handler); RAYAOP_DEBUG_PRINT("Freeing info struct"); dump_memory(info, sizeof(*info)); - efree(info); + rayaop_free(info); RAYAOP_DEBUG_PRINT("Memory freed for intercept info"); } - check_heap_integrity("End of free_intercept_info"); + validate_memory(Z_PTR_P(zv), sizeof(intercept_info), "End of free_intercept_info"); RAYAOP_DEBUG_PRINT("Exiting free_intercept_info"); } @@ -170,9 +228,9 @@ static void rayaop_zend_execute_ex(zend_execute_data *execute_data) zend_function *current_function = execute_data->func; if (current_function->common.scope && current_function->common.function_name) { - zend_string *key = zend_string_alloc(ZSTR_LEN(current_function->common.scope->name) + ZSTR_LEN(current_function->common.function_name) + 2, 0); - sprintf(ZSTR_VAL(key), "%s::%s", ZSTR_VAL(current_function->common.scope->name), ZSTR_VAL(current_function->common.function_name)); - ZSTR_LEN(key) = strlen(ZSTR_VAL(key)); + zend_string *key = rayaop_string_init(ZSTR_VAL(current_function->common.scope->name), ZSTR_LEN(current_function->common.scope->name), 0); + rayaop_string_release(key); + key = rayaop_string_init(ZSTR_VAL(current_function->common.function_name), ZSTR_LEN(current_function->common.function_name), 0); RAYAOP_DEBUG_PRINT("Generated key: %s", ZSTR_VAL(key)); intercept_info *info = zend_hash_find_ptr(intercept_ht, key); @@ -186,7 +244,7 @@ static void rayaop_zend_execute_ex(zend_execute_data *execute_data) if (!Z_OBJ(execute_data->This)) { RAYAOP_DEBUG_PRINT("execute_data->This is not an object, calling original zend_execute_ex"); - safe_zend_string_release(&key); + rayaop_string_release(key); original_zend_execute_ex(execute_data); return; } @@ -223,38 +281,19 @@ static void rayaop_zend_execute_ex(zend_execute_data *execute_data) zval_ptr_dtor(¶ms[2]); is_intercepting = 0; - safe_zend_string_release(&key); + rayaop_string_release(key); return; } } else { RAYAOP_DEBUG_PRINT("No intercept info found for key: %s", ZSTR_VAL(key)); } - safe_zend_string_release(&key); + rayaop_string_release(key); } original_zend_execute_ex(execute_data); } -static int dump_intercept_info(zval *zv, void *arg) -{ - intercept_info *info = Z_PTR_P(zv); - if (info) { - RAYAOP_DEBUG_PRINT("Intercept info:"); - RAYAOP_DEBUG_PRINT(" class_name: %s", info->class_name ? ZSTR_VAL(info->class_name) : "NULL"); - RAYAOP_DEBUG_PRINT(" method_name: %s", info->method_name ? ZSTR_VAL(info->method_name) : "NULL"); - RAYAOP_DEBUG_PRINT(" handler type: %d", Z_TYPE(info->handler)); - - if (info->class_name) { - dump_zend_string(info->class_name); - } - if (info->method_name) { - dump_zend_string(info->method_name); - } - } - return ZEND_HASH_APPLY_KEEP; -} - ZEND_BEGIN_ARG_INFO_EX(arginfo_method_intercept, 0, 0, 3) ZEND_ARG_TYPE_INFO(0, class_name, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, method_name, IS_STRING, 0) @@ -275,29 +314,48 @@ PHP_FUNCTION(method_intercept) Z_PARAM_OBJECT(intercepted) ZEND_PARSE_PARAMETERS_END(); - intercept_info *new_info = ecalloc(1, sizeof(intercept_info)); + intercept_info *new_info = (intercept_info *)rayaop_malloc(sizeof(intercept_info)); if (!new_info) { php_error_docref(NULL, E_ERROR, "Memory allocation failed"); RETURN_FALSE; } RAYAOP_DEBUG_PRINT("Allocated memory for intercept_info"); - new_info->class_name = zend_string_init(class_name, class_name_len, 0); - new_info->method_name = zend_string_init(method_name, method_name_len, 0); + new_info->class_name = rayaop_string_init(class_name, class_name_len, 0); + new_info->method_name = rayaop_string_init(method_name, method_name_len, 0); ZVAL_COPY(&new_info->handler, intercepted); RAYAOP_DEBUG_PRINT("Initialized intercept_info for %s::%s", class_name, method_name); - zend_string *key = zend_string_alloc(class_name_len + method_name_len + 2, 0); - sprintf(ZSTR_VAL(key), "%s::%s", class_name, method_name); - ZSTR_LEN(key) = strlen(ZSTR_VAL(key)); + zend_string *key = rayaop_string_init(class_name, class_name_len, 0); + rayaop_string_release(key); + key = rayaop_string_init(method_name, method_name_len, 0); RAYAOP_DEBUG_PRINT("Registered intercept info for key: %s", ZSTR_VAL(key)); zend_hash_update_ptr(intercept_ht, key, new_info); - safe_zend_string_release(&key); + rayaop_string_release(key); RETURN_TRUE; } +static int dump_intercept_info(zval *zv, void *arg) +{ + intercept_info *info = Z_PTR_P(zv); + if (info) { + RAYAOP_DEBUG_PRINT("Intercept info:"); + RAYAOP_DEBUG_PRINT(" class_name: %s", info->class_name ? ZSTR_VAL(info->class_name) : "NULL"); + RAYAOP_DEBUG_PRINT(" method_name: %s", info->method_name ? ZSTR_VAL(info->method_name) : "NULL"); + RAYAOP_DEBUG_PRINT(" handler type: %d", Z_TYPE(info->handler)); + + if (info->class_name) { + dump_zend_string(info->class_name); + } + if (info->method_name) { + dump_zend_string(info->method_name); + } + } + return ZEND_HASH_APPLY_KEEP; +} + PHP_MINIT_FUNCTION(rayaop) { RAYAOP_DEBUG_PRINT("PHP_MINIT_FUNCTION called"); @@ -305,7 +363,7 @@ PHP_MINIT_FUNCTION(rayaop) original_zend_execute_ex = zend_execute_ex; zend_execute_ex = rayaop_zend_execute_ex; - intercept_ht = pemalloc(sizeof(HashTable), 1); + intercept_ht = (HashTable *)rayaop_malloc(sizeof(HashTable)); if (intercept_ht) { zend_hash_init(intercept_ht, 8, NULL, free_intercept_info, 1); RAYAOP_DEBUG_PRINT("RayAOP extension initialized"); @@ -329,7 +387,7 @@ PHP_MSHUTDOWN_FUNCTION(rayaop) zend_hash_destroy(intercept_ht); RAYAOP_DEBUG_PRINT("Destroyed intercept_ht"); - pefree(intercept_ht, 1); + rayaop_free(intercept_ht); intercept_ht = NULL; RAYAOP_DEBUG_PRINT("Intercept hash table destroyed and freed"); } else { @@ -368,4 +426,4 @@ zend_module_entry rayaop_module_entry = { #ifdef COMPILE_DL_RAYAOP ZEND_GET_MODULE(rayaop) -#endif \ No newline at end of file +#endif