[RFC][PATCH 4/7] kprobe: Dont kfree() from breakpoint context

From: Peter Zijlstra
Date: Thu Aug 27 2020 - 12:21:29 EST


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)