Breakpoint context might not be a safe context for kfree(), push it out to RCU. Signed-off-by: Peter Zijlstra (Intel) <peterz@xxxxxxxxxxxxx> --- include/linux/kprobes.h | 2 +- kernel/kprobes.c | 27 ++++++--------------------- 2 files changed, 7 insertions(+), 22 deletions(-) --- a/include/linux/kprobes.h +++ b/include/linux/kprobes.h @@ -159,6 +159,7 @@ struct kretprobe_instance { union { struct llist_node llist; struct hlist_node hlist; + struct rcu_head rcu; }; struct kretprobe *rp; kprobe_opcode_t *ret_addr; @@ -398,7 +399,6 @@ int register_kretprobes(struct kretprobe void unregister_kretprobes(struct kretprobe **rps, int num); void kprobe_flush_task(struct task_struct *tk); -void recycle_rp_inst(struct kretprobe_instance *ri, struct hlist_head *head); int disable_kprobe(struct kprobe *kp); int enable_kprobe(struct kprobe *kp); --- a/kernel/kprobes.c +++ b/kernel/kprobes.c @@ -1214,8 +1214,7 @@ void kprobes_inc_nmissed_count(struct kp } NOKPROBE_SYMBOL(kprobes_inc_nmissed_count); -void recycle_rp_inst(struct kretprobe_instance *ri, - struct hlist_head *head) +static void recycle_rp_inst(struct kretprobe_instance *ri) { struct kretprobe *rp = ri->rp; @@ -1226,9 +1225,9 @@ void recycle_rp_inst(struct kretprobe_in raw_spin_lock(&rp->lock); hlist_add_head(&ri->hlist, &rp->free_instances); raw_spin_unlock(&rp->lock); - } else - /* Unregistering */ - hlist_add_head(&ri->hlist, head); + } else { + kfree_rcu(ri, rcu); + } } NOKPROBE_SYMBOL(recycle_rp_inst); @@ -1261,15 +1260,12 @@ void kprobe_busy_end(void) void kprobe_flush_task(struct task_struct *tk) { struct kretprobe_instance *ri; - struct hlist_head empty_rp; struct llist_node *node; - struct hlist_node *tmp; /* Early boot, not yet initialized. */ if (unlikely(!kprobes_initialized)) return; - INIT_HLIST_HEAD(&empty_rp); kprobe_busy_begin(); @@ -1280,12 +1276,7 @@ void kprobe_flush_task(struct task_struc ri = container_of(node, struct kretprobe_instance, llist); node = node->next; - recycle_rp_inst(ri, &empty_rp); - } - - hlist_for_each_entry_safe(ri, tmp, &empty_rp, hlist) { - hlist_del(&ri->hlist); - kfree(ri); + recycle_rp_inst(ri); } kprobe_busy_end(); @@ -1937,7 +1928,6 @@ unsigned long __kretprobe_trampoline_han unsigned long orig_ret_address = 0; struct llist_node *first, *node; struct hlist_head empty_rp; - struct hlist_node *tmp; INIT_HLIST_HEAD(&empty_rp); @@ -1980,16 +1970,11 @@ unsigned long __kretprobe_trampoline_han __this_cpu_write(current_kprobe, &kprobe_busy); } - recycle_rp_inst(ri, &empty_rp); + recycle_rp_inst(ri); first = node; } - hlist_for_each_entry_safe(ri, tmp, &empty_rp, hlist) { - hlist_del(&ri->hlist); - kfree(ri); - } - return orig_ret_address; } NOKPROBE_SYMBOL(__kretprobe_trampoline_handler)