
Offering: HULK hulk inclusion category: bugfix bugzilla: https://codehub-y.huawei.com/hulk/bugzilla/issues/196 -------------------------------- There is following deadlock issue: CPU0 CPU1 ==== ==== kretprobe_trampoline kmem_cache_alloc trampoline_handler kmemleak_alloc __kretprobe_trampoline_handler create_object kretprobe_hash_lock <-- hold kmemleak lock <-- hold kretprobe table lock __link_object recycle_rp_inst stack_trace_save kfree_rcu kvfree_call_rcu ... kmemleak_ignore unwind_next_frame kretprobe_find_ret_addr <-- wait for kmemleak lock kretprobe_hash_lock <-- wait for kretprobe table lock One task on CPU0 hold kretprobe_hash_lock and wait for kmemleak_lock, however, kmemleak_lock was held by other task on CPU1 and that task is waiting for kretprobe_hash_lock, then deadlock happended. To fix it, move kfree_rcu() out of kretprobe table lock area. Fixes: b67815b05d67 ("[Backport] kprobes: Add kretprobe_find_ret_addr() for searching return address") Signed-off-by: Zheng Yejian <zhengyejian1@huawei.com> --- kernel/kprobes.c | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/kernel/kprobes.c b/kernel/kprobes.c index dacb71214fa1..5d64d97975ba 100644 --- a/kernel/kprobes.c +++ b/kernel/kprobes.c @@ -1223,7 +1223,7 @@ void kprobes_inc_nmissed_count(struct kprobe *p) } NOKPROBE_SYMBOL(kprobes_inc_nmissed_count); -static void recycle_rp_inst(struct kretprobe_instance *ri) +static void recycle_rp_inst(struct kretprobe_instance *ri, struct hlist_head *head) { struct kretprobe *rp = ri->rp; @@ -1235,7 +1235,7 @@ static void recycle_rp_inst(struct kretprobe_instance *ri) hlist_add_head(&ri->hlist, &rp->free_instances); raw_spin_unlock(&rp->lock); } else - kfree_rcu(ri, rcu); + hlist_add_head(&ri->hlist, head); } NOKPROBE_SYMBOL(recycle_rp_inst); @@ -1326,6 +1326,7 @@ void kprobe_flush_task(struct task_struct *tk) struct hlist_head *head; struct hlist_node *tmp; unsigned long hash, flags = 0; + HLIST_HEAD(empty_rp); if (unlikely(!kprobes_initialized)) /* Early boot. kretprobe_table_locks not yet initialized. */ @@ -1338,10 +1339,16 @@ void kprobe_flush_task(struct task_struct *tk) kretprobe_table_lock(hash, &flags); hlist_for_each_entry_safe(ri, tmp, head, hlist) { if (ri->task == tk) - recycle_rp_inst(ri); + recycle_rp_inst(ri, &empty_rp); } kretprobe_table_unlock(hash, &flags); + hlist_for_each_entry_safe(ri, tmp, &empty_rp, hlist) { + hlist_del(&ri->hlist); + INIT_HLIST_NODE(&ri->hlist); + kfree_rcu(ri, rcu); + } + kprobe_busy_end(); } NOKPROBE_SYMBOL(kprobe_flush_task); @@ -2014,6 +2021,7 @@ unsigned long __kretprobe_trampoline_handler(struct pt_regs *regs, unsigned long flags; kprobe_opcode_t *correct_ret_addr = NULL; bool skipped = false; + HLIST_HEAD(empty_rp); kretprobe_hash_lock(current, &head, &flags); @@ -2082,7 +2090,7 @@ unsigned long __kretprobe_trampoline_handler(struct pt_regs *regs, __this_cpu_write(current_kprobe, prev); } - recycle_rp_inst(ri); + recycle_rp_inst(ri, &empty_rp); if (ri == last) break; @@ -2090,6 +2098,12 @@ unsigned long __kretprobe_trampoline_handler(struct pt_regs *regs, kretprobe_hash_unlock(current, &flags); + hlist_for_each_entry_safe(ri, tmp, &empty_rp, hlist) { + hlist_del(&ri->hlist); + INIT_HLIST_NODE(&ri->hlist); + kfree_rcu(ri, rcu); + } + return (unsigned long)correct_ret_addr; } NOKPROBE_SYMBOL(__kretprobe_trampoline_handler) -- 2.25.1