From 78a3217159392ec122aa91e054cdefdd99c84b07 Mon Sep 17 00:00:00 2001 From: eshiner Date: Wed, 22 Oct 2025 17:27:29 +0800 Subject: [PATCH 1/2] upatch-manage: fix cannot find overrided patch symbol Signed-off-by: eshiner --- upatch-manage/patch_manage.c | 29 +++++++++++--------------- upatch-manage/process_entity.c | 37 ++++++++++++++++------------------ upatch-manage/process_entity.h | 4 ++-- 3 files changed, 31 insertions(+), 39 deletions(-) diff --git a/upatch-manage/patch_manage.c b/upatch-manage/patch_manage.c index 12409f1..fa4ccf9 100644 --- a/upatch-manage/patch_manage.c +++ b/upatch-manage/patch_manage.c @@ -131,9 +131,9 @@ static int upatch_uprobe_handler(struct uprobe_consumer *self, struct pt_regs *r struct inode *inode = NULL; struct target_entity *target = NULL; struct process_entity *process = NULL; - struct patch_entity *actived_patch = NULL; + struct patch_entity *patch = NULL; + struct patch_entity *patch_tmp = NULL; - struct patch_info *patch_info; unsigned long jump_addr; int ret = UPROBE_RUN_OLD_FUNC; @@ -166,31 +166,27 @@ static int upatch_uprobe_handler(struct uprobe_consumer *self, struct pt_regs *r spin_lock(&process->thread_lock); // ensure only one thread could check & resolve patch - /* Step 5: Get actived patch entity of the target */ - actived_patch = target_get_actived_patch(target); - if (unlikely(!actived_patch)) { - // target does not have any actived patch yet - goto unlock_out; - } - - /* Step 6. Check or resolve patch of the process */ - patch_info = process_switch_and_get_patch(process, actived_patch); - if (unlikely(!patch_info)) { - ret = upatch_resolve(target, actived_patch, process, vma_start); + /* Step 5: Process load all actived patches in target */ + list_for_each_entry_safe(patch, patch_tmp, &target->actived_patches, actived_node) { + if (likely(process_find_patch(process, patch))) { + continue; + } + + ret = upatch_resolve(target, patch, process, vma_start); if (unlikely(ret)) { - log_err("process %d: failed to resolve patch %s, ret=%d\n", tgid, actived_patch->file.path, ret); + log_err("process %d: failed to resolve patch %s, ret=%d\n", tgid, patch->file.path, ret); goto unlock_out; } } - /* Step 7: Find patch function jump addr */ + /* Step 6: Find patch function jump addr */ jump_addr = process_get_jump_addr(process, pc); if (unlikely(!jump_addr)) { log_err("process %d: cannot find jump address, pc=0x%lx\n", tgid, pc); goto unlock_out; } - /* Step 8: Set patch function jump addr to pc register */ + /* Step 7: Set patch function jump addr to pc register */ instruction_pointer_set(regs, jump_addr); log_debug("process %d: jump 0x%lx -> 0x%lx\n", tgid, pc, jump_addr); @@ -198,7 +194,6 @@ unlock_out: spin_unlock(&process->thread_lock); release_out: - put_patch(actived_patch); put_process(process); put_target(target); iput(inode); diff --git a/upatch-manage/process_entity.c b/upatch-manage/process_entity.c index a356898..bc933c8 100644 --- a/upatch-manage/process_entity.c +++ b/upatch-manage/process_entity.c @@ -236,42 +236,39 @@ void release_process(struct kref *kref) kmem_cache_free(g_process_cache, process); } -struct patch_info *process_switch_and_get_patch(struct process_entity *process, struct patch_entity *patch) +struct patch_info *process_find_patch(struct process_entity *process, struct patch_entity *patch) { - struct patch_info *patch_info; - - BUG_ON(unlikely(!process || !patch)); - - if (likely(process->patch_info && process->patch_info->patch == patch)) { - return process->patch_info; - } - - patch_info = find_patch_info_unlocked(process, patch); - if (unlikely(!patch_info)) { + if (unlikely(!process || !patch)) { return NULL; } - process->patch_info = patch_info; - - return patch_info; + return find_patch_info_unlocked(process, patch); } unsigned long process_get_jump_addr(struct process_entity *process, unsigned long old_addr) { + struct patch_info *patch; struct patch_jump_entry *entry; unsigned long jump_addr = 0; + unsigned long addr_hash = 0; - if (unlikely(!process || !process->patch_info)) { + if (unlikely(!process)) { return 0; } - hash_for_each_possible(process->patch_info->jump_table, entry, node, hash_long(old_addr, PATCH_FUNC_HASH_BITS)) { - if (entry->old_addr == old_addr) { - jump_addr = entry->new_addr; - break; + // calculate address hash + addr_hash = hash_long(old_addr, PATCH_FUNC_HASH_BITS); + + // find jump address from all active patches + list_for_each_entry(patch, &process->patch_list, node) { + hash_for_each_possible(patch->jump_table, entry, node, addr_hash) { + if (entry->old_addr == old_addr) { + jump_addr = entry->new_addr; + break; + } } } - + return jump_addr; } diff --git a/upatch-manage/process_entity.h b/upatch-manage/process_entity.h index 53f172e..6d09a3b 100644 --- a/upatch-manage/process_entity.h +++ b/upatch-manage/process_entity.h @@ -155,14 +155,14 @@ static inline bool process_is_alive(struct process_entity *process) } /** - * @brief Switch and get process actived patch to specific one + * @brief Find process patch info by patch entity * @param process: Process entity (must not NULL) * @param patch: Patch entity (must not NULL) * @return Patch info pointer, NULL if not found * * Caller must hold thread lock. */ -struct patch_info *process_switch_and_get_patch(struct process_entity *process, struct patch_entity *patch); +struct patch_info *process_find_patch(struct process_entity *process, struct patch_entity *patch); /** * @brief Find function jump address in the process -- Gitee From f7cc74be8e75891d10c49caf502d88dbe6ab1b40 Mon Sep 17 00:00:00 2001 From: eshiner Date: Thu, 23 Oct 2025 02:48:50 +0800 Subject: [PATCH 2/2] upatch-manage: use mm_struct pointer as process lookup key Signed-off-by: eshiner --- upatch-manage/process_entity.c | 102 ++++++++++++++++++++++++--------- upatch-manage/process_entity.h | 15 ++--- upatch-manage/target_entity.c | 72 +++++++++++++---------- upatch-manage/target_entity.h | 10 ---- 4 files changed, 121 insertions(+), 78 deletions(-) diff --git a/upatch-manage/process_entity.c b/upatch-manage/process_entity.c index bc933c8..cf3af82 100644 --- a/upatch-manage/process_entity.c +++ b/upatch-manage/process_entity.c @@ -60,39 +60,30 @@ static int do_free_patch_memory(struct mm_struct *mm, unsigned long addr, size_t return upatch_munmap(mm, addr, len, NULL); } -static void free_patch_memory(struct task_struct *task, struct patch_info *patch) +static void free_patch_memory(struct process_entity *process, struct patch_info *patch) { - pid_t pid = task_tgid_nr(task); - struct mm_struct *mm; - int ret; - mm = get_task_mm(task); - if (unlikely(!mm)) { - return; - } - - mmap_write_lock(mm); + mmap_write_lock(process->mm); log_debug("process %d: free patch text, addr=0x%lx, len=0x%lx\n", - pid, patch->text_addr, patch->text_len); - ret = do_free_patch_memory(mm, patch->text_addr, patch->text_len); + process->tgid, patch->text_addr, patch->text_len); + ret = do_free_patch_memory(process->mm, patch->text_addr, patch->text_len); if (ret) { log_err("failed to free patch text, pid=%d, addr=0x%lx, len=0x%lx, ret=%d\n", - pid, patch->text_addr, patch->text_len, ret); + process->tgid, patch->text_addr, patch->text_len, ret); } log_debug("process %d: free patch rodata, addr=0x%lx, len=0x%lx\n", - pid, patch->rodata_addr, patch->rodata_len); - ret = do_free_patch_memory(mm, patch->rodata_addr, patch->rodata_len); + process->tgid, patch->rodata_addr, patch->rodata_len); + ret = do_free_patch_memory(process->mm, patch->rodata_addr, patch->rodata_len); if (ret) { log_err("failed to free patch rodata, pid=%d, addr=0x%lx, len=0x%lx, ret=%d\n", - pid, patch->rodata_addr, patch->rodata_len, ret); + process->tgid, patch->rodata_addr, patch->rodata_len, ret); } - mmap_write_unlock(mm); + mmap_write_unlock(process->mm); - mmput(mm); return; } @@ -172,6 +163,34 @@ static bool is_patch_removable(pid_t pid, const void *page, void *context) return true; } +static struct task_struct *process_get_task(struct process_entity *process) +{ + struct pid *pid; + struct task_struct *task; + + if (unlikely(!process)) { + return NULL; + } + + pid = find_get_pid(process->tgid); + if (!pid) { + return NULL; + } + + rcu_read_lock(); + + task = pid_task(pid, PIDTYPE_PID); + if (task) { + get_task_struct(task); + } + + rcu_read_unlock(); + + put_pid(pid); + + return task; +}; + /* --- Public interface --- */ struct process_entity *new_process(struct task_struct *task) @@ -187,16 +206,14 @@ struct process_entity *new_process(struct task_struct *task) return ERR_PTR(-ENOMEM); } - process->task = get_task_struct(task); process->tgid = task_tgid_nr(task); + process->mm = get_task_mm(task); spin_lock_init(&process->thread_lock); INIT_HLIST_NODE(&process->node); INIT_LIST_HEAD(&process->pending_node); - INIT_LIST_HEAD(&process->patch_list); - process->patch_info = NULL; kref_init(&process->kref); @@ -224,18 +241,41 @@ void release_process(struct kref *kref) list_for_each_entry_safe(patch_info, tmp, &process->patch_list, node) { list_del_init(&patch_info->node); - free_patch_memory(process->task, patch_info); + + free_patch_memory(process, patch_info); free_patch_info(patch_info); } - process->patch_info = NULL; - put_task_struct(process->task); - process->task = NULL; + mmput(process->mm); + process->mm = NULL; process->tgid = 0; kmem_cache_free(g_process_cache, process); } +bool process_is_alive(struct process_entity *process) +{ + struct pid *pid; + struct task_struct *task; + + if (unlikely(!process)) { + return false; + } + + pid = find_get_pid(process->tgid); + if (!pid) { + return false; + } + + rcu_read_lock(); + + task = pid_task(pid, PIDTYPE_PID); + rcu_read_unlock(); + + put_pid(pid); + return task != NULL; +} + struct patch_info *process_find_patch(struct process_entity *process, struct patch_entity *patch) { if (unlikely(!process || !patch)) { @@ -336,7 +376,6 @@ int process_load_patch(struct process_entity *process, struct patch_entity *patc } list_add(&patch_info->node, &process->patch_list); - process->patch_info = patch_info; return 0; } @@ -351,13 +390,15 @@ void process_remove_patch(struct process_entity *process, struct patch_entity *p } list_del_init(&patch_info->node); - free_patch_memory(process->task, patch_info); + + free_patch_memory(process, patch_info); free_patch_info(patch_info); } int process_check_patch_on_stack(struct process_entity *process, struct patch_entity *patch) { struct patch_info *patch_info; + struct task_struct *task; if (unlikely(!process || !patch)) { return -EINVAL; @@ -368,5 +409,10 @@ int process_check_patch_on_stack(struct process_entity *process, struct patch_en return 0; } - return check_process_stack(process->task, is_patch_removable, patch_info); + task = process_get_task(process); + if (unlikely(!task)) { + return 0; + } + + return check_process_stack(task, is_patch_removable, patch_info); } diff --git a/upatch-manage/process_entity.h b/upatch-manage/process_entity.h index 6d09a3b..adca782 100644 --- a/upatch-manage/process_entity.h +++ b/upatch-manage/process_entity.h @@ -24,7 +24,8 @@ #include #include -#include +#include +#include #include #include @@ -66,7 +67,7 @@ struct patch_info { // target may be loaded into different process // due to latency of uprobe handle, process may dealy patch loading struct process_entity { - struct task_struct *task; // underlying task struct + struct mm_struct *mm; // underlying mm struct pid_t tgid; spinlock_t thread_lock; // thread lock @@ -75,7 +76,6 @@ struct process_entity { struct list_head pending_node; // pending list node struct list_head patch_list; // all actived patches - struct patch_info *patch_info; // current actived patch info struct kref kref; }; @@ -145,14 +145,7 @@ static inline void put_process(struct process_entity *process) * * Safe to call with NULL; it will be treated as not alive. */ -static inline bool process_is_alive(struct process_entity *process) -{ - if (unlikely(!process || !process->task)) { - return false; - } - - return pid_alive(process->task); -} +bool process_is_alive(struct process_entity *process); /** * @brief Find process patch info by patch entity diff --git a/upatch-manage/target_entity.c b/upatch-manage/target_entity.c index 099d7a4..e370ca8 100644 --- a/upatch-manage/target_entity.c +++ b/upatch-manage/target_entity.c @@ -566,12 +566,12 @@ static int register_patch_functions_unlocked(struct target_entity *target, const /* --- Process management --- */ -static inline struct process_entity *find_process_unlocked(const struct target_entity *target, pid_t pid) +static inline struct process_entity *find_process_unlocked(const struct target_entity *target, struct mm_struct *mm) { struct process_entity *process; - hash_for_each_possible(target->processes, process, node, hash_32(pid, PROCESS_HASH_BITS)) { - if (process->tgid == pid) { + hash_for_each_possible(target->processes, process, node, hash_ptr(mm, PROCESS_HASH_BITS)) { + if (process->mm == mm) { return process; } } @@ -888,47 +888,61 @@ unlock_out: return ret; } -struct patch_entity *target_get_actived_patch(struct target_entity *target) -{ - struct patch_entity *patch; - - spin_lock(&target->active_lock); - patch = get_patch(list_first_entry_or_null(&target->actived_patches, struct patch_entity, actived_node)); - spin_unlock(&target->active_lock); - - return patch; -} - struct process_entity *target_get_process(struct target_entity *target, struct task_struct *task) { - struct process_entity *process; - pid_t pid = task_tgid_nr(task); + struct process_entity *curr_proc = NULL; + struct process_entity *new_proc = NULL; + struct mm_struct *mm = NULL; - if (unlikely(!target)) { + if (unlikely(!target || !task)) { return ERR_PTR(-EINVAL); } + mm = get_task_mm(task); + if (unlikely(!mm)) { + return NULL; + } + + /* 1.Lock to find process entity under lock */ spin_lock(&target->process_lock); - process = find_process_unlocked(target, pid); - if (!process) { - log_debug("create process %d for '%s'\n", pid, target->file.path); + curr_proc = find_process_unlocked(target, mm); + if (curr_proc) { + get_process(curr_proc); + spin_unlock(&target->process_lock); + goto out; + } - process = new_process(task); - if (IS_ERR(process)) { - log_err("failed to create target process, ret=%d\n", (int)PTR_ERR(process)); - goto unlock_out; - } + spin_unlock(&target->process_lock); - hash_add(target->processes, &process->node, hash_32(pid, PROCESS_HASH_BITS)); + /* 2.Alloc new process */ + new_proc = new_process(task); + if (unlikely(IS_ERR(new_proc))) { + curr_proc = new_proc; + goto out; } - get_process(process); + /* 3.Lock again to insert new process */ + spin_lock(&target->process_lock); -unlock_out: + curr_proc = find_process_unlocked(target, mm); + if (curr_proc) { + get_process(curr_proc); + } else { + hash_add(target->processes, &new_proc->node, hash_ptr(mm, PROCESS_HASH_BITS)); + get_process(new_proc); + + curr_proc = new_proc; + new_proc = NULL; + } + + spin_unlock(&target->process_lock); + +out: spin_unlock(&target->process_lock); + mmput(mm); - return process; + return curr_proc; } void target_cleanup_process(struct target_entity *target) diff --git a/upatch-manage/target_entity.h b/upatch-manage/target_entity.h index 6f07f84..ec03e39 100644 --- a/upatch-manage/target_entity.h +++ b/upatch-manage/target_entity.h @@ -217,16 +217,6 @@ int target_deactive_patch(struct target_entity *target, struct inode *inode, str */ enum upatch_status target_patch_status(struct target_entity *target, const struct inode *inode); -/** - * @brief Get current actived patch on the target - * @param target Target entity - * @return Current actived patch entity or NULL if none exists - * - * The returned patch has its reference count incremented. - * Caller must call put_patch() when done. - */ -struct patch_entity *target_get_actived_patch(struct target_entity *target); - /** * @brief Get or create process entity * @param target Target entity -- Gitee