kprobe源码分析
Table of contents
Open Table of contents
1. 环境配置
源码版本:7.2.0,Arm64
可以直接使用内核提供的示例模块:
Kernel hacking --->
[*] Sample kernel code --->
<M> Build kprobes examples -- loadable modules only
<M> Build kretprobes example -- loadable modules only
在kprobe_example驱动注册时会调用 register_kprobe 在 kernel_clone 上注册,在注销时会移除:
static int __init kprobe_init(void)
{
int ret;
kp.pre_handler = handler_pre;
kp.post_handler = handler_post;
ret = register_kprobe(&kp);
if (ret < 0) {
pr_err("register_kprobe failed, returned %d\n", ret);
return ret;
}
pr_info("Planted kprobe at %p\n", kp.addr);
return 0;
}
static void __exit kprobe_exit(void)
{
unregister_kprobe(&kp);
pr_info("kprobe at %p unregistered\n", kp.addr);
}
kretprobe_example驱动也类似:
static struct kretprobe my_kretprobe = {
.handler = ret_handler,
.entry_handler = entry_handler,
.data_size = sizeof(struct my_data),
/* Probe up to 20 instances concurrently. */
.maxactive = 20,
};
static int __init kretprobe_init(void)
{
int ret;
my_kretprobe.kp.symbol_name = func_name;
ret = register_kretprobe(&my_kretprobe);
if (ret < 0) {
pr_err("register_kretprobe failed, returned %d\n", ret);
return ret;
}
pr_info("Planted return probe at %s: %p\n",
my_kretprobe.kp.symbol_name, my_kretprobe.kp.addr);
return 0;
}
static void __exit kretprobe_exit(void)
{
unregister_kretprobe(&my_kretprobe);
pr_info("kretprobe at %p unregistered\n", my_kretprobe.kp.addr);
/* nmissed > 0 suggests that maxactive was set too low. */
pr_info("Missed probing %d instances of %s\n",
my_kretprobe.nmissed, my_kretprobe.kp.symbol_name);
}
2. 注册kprobe
2.1 准备工作
int register_kprobe(struct kprobe *p)
{
int ret;
struct module *probed_mod;
kprobe_opcode_t *addr;
bool on_func_entry;
// 从kallsyms中,根据 函数名与offset 获取插入点的最终地址
addr = _kprobe_addr(p->addr, p->symbol_name, p->offset, &on_func_entry);
if (IS_ERR(addr))
return PTR_ERR(addr);
p->addr = addr;
// 防止重复调用
ret = warn_kprobe_rereg(p);
if (ret)
return ret;
// 初始化 flags
p->flags &= KPROBE_FLAG_DISABLED;
if (on_func_entry)
p->flags |= KPROBE_FLAG_ON_FUNC_ENTRY;
p->nmissed = 0;
INIT_LIST_HEAD(&p->list);
// 插入点地址检查,不允许修改ftrace loaction / jump_label / static_call 等区域代码
ret = check_kprobe_address_safe(p, &probed_mod);
if (ret)
return ret;
// 注册kprobe
ret = __register_kprobe(p);
if (probed_mod)
module_put(probed_mod);
return ret;
}
static int __register_kprobe(struct kprobe *p)
{
int ret;
struct kprobe *old_p;
guard(mutex)(&kprobe_mutex);
// 该地址已经挂过kprobe,直接聚合
old_p = get_kprobe(p->addr);
if (old_p)
/* Since this may unoptimize 'old_p', locking 'text_mutex'. */
return register_aggr_kprobe(old_p, p);
// 该地址没挂过kprobe,
scoped_guard(cpus_read_lock) {
/* Prevent text modification */
guard(mutex)(&text_mutex);
// 将地址上原来的指令放入p->opcode,根据指令类型判断是用 XOL / Simulate
ret = prepare_kprobe(p);
if (ret)
return ret;
}
// 将该kprobe结构体放入全局的HashTable中,key是挂载地址
INIT_HLIST_NODE(&p->hlist);
hlist_add_head_rcu(&p->hlist,
&kprobe_table[hash_ptr(p->addr, KPROBE_HASH_BITS)]);
if (!kprobes_all_disarmed && !kprobe_disabled(p)) {
// 将原来的指令patch为 BRK#4
ret = arm_kprobe(p);
if (ret) {
hlist_del_rcu(&p->hlist);
synchronize_rcu();
}
}
/* Try to optimize kprobe */
try_to_optimize_kprobe(p);
return 0;
}
int __kprobes arch_prepare_kprobe(struct kprobe *p)
{
unsigned long probe_addr = (unsigned long)p->addr;
// arm64下的指令都是4字节对齐的,如果未对齐说明之前计算出的地址错误
if (probe_addr & 0x3)
return -EINVAL;
// 保存入口指令
p->opcode = *p->addr;
// 该指令不能在异常处理表中
if (search_exception_tables(probe_addr))
return -EINVAL;
// 判断指令类型以进行后续处理
// 如果判断出该指令需要Simulate,那么会根据指令的类型选择对应的hanler挂载到kprobe结构体上
switch (arm_kprobe_decode_insn(p->addr, &p->ainsn)) {
case INSN_REJECTED: // 不支持的指令,返回错误
return -EINVAL;
case INSN_GOOD_NO_SLOT: // 该指令需要使用hanler进行模拟
p->ainsn.xol_insn = NULL;
break;
case INSN_GOOD: // 该指令使用XOL,先获取一个空slot
p->ainsn.xol_insn = get_insn_slot();
if (!p->ainsn.xol_insn)
return -ENOMEM;
break;
}
/* prepare the instruction */
if (p->ainsn.xol_insn)
arch_prepare_ss_slot(p);
else
arch_prepare_simulate(p);
return 0;
}
2.2 处理流程
kernel_clone 的第一条指令是mov,可以使用XOL来实现:
-exec x/10xi p->addr
0xffff8000800435b8 <kernel_clone>: mov x9, x30
0xffff8000800435bc <kernel_clone+4>: nop
0xffff8000800435c0 <kernel_clone+8>: stp x29, x30, [sp, #-112]!
0xffff8000800435c4 <kernel_clone+12>: mov x29, sp
0xffff8000800435c8 <kernel_clone+16>: stp x19, x20, [sp, #16]
0xffff8000800435cc <kernel_clone+20>: stp x21, x22, [sp, #32]
0xffff8000800435d0 <kernel_clone+24>: mov x21, x0
0xffff8000800435d4 <kernel_clone+28>: stp x23, x24, [sp, #48]
0xffff8000800435d8 <kernel_clone+32>: ldr x20, [x0]
0xffff8000800435dc <kernel_clone+36>: stp xzr, xzr, [sp, #80]
在arch_prepare_ss_slot中对前面分配的空slot进行填充:
static void __kprobes arch_prepare_ss_slot(struct kprobe *p)
{
kprobe_opcode_t *addr = p->ainsn.xol_insn;
// 在slot内依次放入mov指令 与 BRK_SS
aarch64_insn_patch_text_nosync(addr, le32_to_cpu(p->opcode));
aarch64_insn_patch_text_nosync(addr + 1, BRK64_OPCODE_KPROBES_SS);
// 将mov的后一条指令保存
p->ainsn.xol_restore = (unsigned long) p->addr +
sizeof(kprobe_opcode_t);
}
而在arch_prepare_simulate中无需保存下一条指令的地址,因为中断返回后会自动到下一条指令执行(无需到slot上执行也就不会破坏pt_regs->pc):
static void __kprobes arch_prepare_simulate(struct kprobe *p)
{
/* This instructions is not executed xol. No need to adjust the PC */
p->ainsn.xol_restore = 0;
}
无论使用哪种方式,arm_kprobe 都会将mov指令替换:
-exec x/10xi p->addr
0xffff8000800435b8 <kernel_clone>: brk #0x4
0xffff8000800435bc <kernel_clone+4>: nop
0xffff8000800435c0 <kernel_clone+8>: stp x29, x30, [sp, #-112]!
0xffff8000800435c4 <kernel_clone+12>: mov x29, sp
0xffff8000800435c8 <kernel_clone+16>: stp x19, x20, [sp, #16]
0xffff8000800435cc <kernel_clone+20>: stp x21, x22, [sp, #32]
0xffff8000800435d0 <kernel_clone+24>: mov x21, x0
0xffff8000800435d4 <kernel_clone+28>: stp x23, x24, [sp, #48]
0xffff8000800435d8 <kernel_clone+32>: ldr x20, [x0]
0xffff8000800435dc <kernel_clone+36>: stp xzr, xzr, [sp, #80]
那么接下来只要触发kernel_clone,就会来到中断向量表中找到同步中断进行处理:
SYM_CODE_START(vectors)
......
kernel_ventry 1, h, 64, sync // Synchronous EL1h
kernel_ventry 1, h, 64, irq // IRQ EL1h
kernel_ventry 1, h, 64, fiq // FIQ EL1h
kernel_ventry 1, h, 64, error // Error EL1h
......
SYM_CODE_END(vectors)
在el1h_64_sync_handler 中根据ESR进行异常分发,然后在call_el1_break_hook中进行BRK的分发:
if (IS_ENABLED(CONFIG_KPROBES)) {
if (esr_brk_comment(esr) == KPROBES_BRK_IMM)
return kprobe_brk_handler(regs, esr);
if (esr_brk_comment(esr) == KPROBES_BRK_SS_IMM)
return kprobe_ss_brk_handler(regs, esr);
}
终于来到了kprobe的处理阶段,
int __kprobes
kprobe_brk_handler(struct pt_regs *regs, unsigned long esr)
{
struct kprobe *p, *cur_kprobe;
struct kprobe_ctlblk *kcb;
unsigned long addr = instruction_pointer(regs);
kcb = get_kprobe_ctlblk();
cur_kprobe = kprobe_running();
// 从HashTable中拿到当前地址对应的kprobe结构体
p = get_kprobe((kprobe_opcode_t *) addr);
if (WARN_ON_ONCE(!p)) {
return DBG_HOOK_ERROR;
}
// 递归嵌套
if (cur_kprobe) {
/* Hit a kprobe inside another kprobe */
if (!reenter_kprobe(p, regs, kcb))
return DBG_HOOK_ERROR;
} else {
/* Probe hit */
set_current_kprobe(p);
kcb->kprobe_status = KPROBE_HIT_ACTIVE;
// 调用模块中注册的pre_handler,根据返回值判断:
// 1. 如果返回0则接管程序流
// 2. 如果返回非0,说明pre_handler中已经对原指令进行的处理,不再使用XOL / Simulate
if (!p->pre_handler || !p->pre_handler(p, regs))
setup_singlestep(p, regs, kcb, 0);
else
reset_current_kprobe();
}
return DBG_HOOK_HANDLED;
}
使用setup_singlestep进行后续处理:
static void __kprobes setup_singlestep(struct kprobe *p,
struct pt_regs *regs,
struct kprobe_ctlblk *kcb, int reenter)
{
unsigned long slot;
if (reenter) {
save_previous_kprobe(kcb);
set_current_kprobe(p);
kcb->kprobe_status = KPROBE_REENTER;
} else {
kcb->kprobe_status = KPROBE_HIT_SS;
}
if (p->ainsn.xol_insn) {
/* prepare for single stepping */
slot = (unsigned long)p->ainsn.xol_insn;
kprobes_save_local_irqflag(kcb, regs);
// 直接将pt_regs->pc 修改为slot,然后中断返回
instruction_pointer_set(regs, slot);
} else {
// 在中断处理环境下模拟指令
arch_simulate_insn(p, regs);
}
}
使用XOL时,执行完第一条指令后就会执行BRK_SS再次进入同步异常,来到:
kprobe_ss_brk_handler(struct pt_regs *regs, unsigned long esr)
{
struct kprobe_ctlblk *kcb = get_kprobe_ctlblk();
unsigned long addr = instruction_pointer(regs);
struct kprobe *cur = kprobe_running();
if (cur && (kcb->kprobe_status & (KPROBE_HIT_SS | KPROBE_REENTER)) &&
((unsigned long)&cur->ainsn.xol_insn[1] == addr)) {
kprobes_restore_local_irqflag(kcb, regs);
// 将pt_regs->pc 直接修改为kprobe->ainsn.xol_restore 中保存的下一条指令的地址
// 然后调用注册好的post_handler,最后中断返回
post_kprobe_handler(cur, kcb, regs);
return DBG_HOOK_HANDLED;
}
/* not ours, kprobes should ignore it */
return DBG_HOOK_ERROR;
}
使用Simulate时,直接调用之前配置的hanler模拟指令执行,然后同样调用post_kprobe_handler:
static void __kprobes arch_simulate_insn(struct kprobe *p, struct pt_regs *regs)
{
struct kprobe_ctlblk *kcb = get_kprobe_ctlblk();
if (p->ainsn.api.handler)
p->ainsn.api.handler(le32_to_cpu(p->opcode), (long)p->addr, regs);
/* single step simulated, now go for post processing */
post_kprobe_handler(p, kcb, regs);
}
2.3 聚合注册
当在一个指令地址上注册了多个kprobe时,Linux不会在同一个地址重复 patch 多次 BRK,而是把这些 probe 聚合到一个 aggr_kprobe 上。
/*
* This registers the second or subsequent kprobe at the same address.
*/
static int register_aggr_kprobe(struct kprobe *orig_p, struct kprobe *p)
{
int ret = 0;
struct kprobe *ap = orig_p;
scoped_guard(cpus_read_lock) {
/* For preparing optimization, jump_label_text_reserved() is called */
guard(jump_label_lock)();
guard(mutex)(&text_mutex);
if (!kprobe_aggrprobe(orig_p)) {
// 如果原probe不是aggr_probe(即当前probe是第二个注册的),那么创建一个aggr_probe
ap = alloc_aggr_kprobe(orig_p);
if (!ap)
return -ENOMEM;
init_aggr_kprobe(ap, orig_p);
} else if (kprobe_unused(ap)) {
// 已经聚合但是准备销毁,那么取消销毁
ret = reuse_unused_kprobe(ap);
if (ret)
return ret;
}
// 处理特殊状态
if (kprobe_gone(ap)) {
......
}
// 将新的probe添加到ap中
copy_kprobe(ap, p);
ret = add_new_kprobe(ap, p);
}
if (ret == 0 && kprobe_disabled(ap) && !kprobe_disabled(p)) {
ap->flags &= ~KPROBE_FLAG_DISABLED;
if (!kprobes_all_disarmed) {
/* Arm the breakpoint again. */
ret = arm_kprobe(ap);
if (ret) {
ap->flags |= KPROBE_FLAG_DISABLED;
list_del_rcu(&p->list);
synchronize_rcu();
}
}
}
return ret;
}
使用原来的kprobe初始化一个聚合kprobe:
static void init_aggr_kprobe(struct kprobe *ap, struct kprobe *p)
{
// 将p中的opcode和ainsn复制过来
copy_kprobe(p, ap);
flush_insn_slot(ap);
ap->addr = p->addr;
ap->flags = p->flags & ~KPROBE_FLAG_OPTIMIZED;
// 更新pre_handler 与 post_handler
// aggr_pre_handler中会调用所有被聚合的kprobe中的handler
ap->pre_handler = aggr_pre_handler;
/* We don't care the kprobe which has gone. */
if (p->post_handler && !kprobe_gone(p))
ap->post_handler = aggr_post_handler;
INIT_LIST_HEAD(&ap->list);
INIT_HLIST_NODE(&ap->hlist);
// 将当前p添加到ap
list_add_rcu(&p->list, &ap->list);
// 将全局HashTable中的val改成ap
hlist_replace_rcu(&p->hlist, &ap->hlist);
}
将新的kprobe添加到ap:
static int add_new_kprobe(struct kprobe *ap, struct kprobe *p)
{
if (p->post_handler)
unoptimize_kprobe(ap, true); /* Fall back to normal kprobe */
list_add_rcu(&p->list, &ap->list);
if (p->post_handler && !ap->post_handler)
ap->post_handler = aggr_post_handler;
return 0;
}
原有的处理流程不变,只是在调用handler时,ap的handler会调用所有的handler:
static int aggr_pre_handler(struct kprobe *p, struct pt_regs *regs)
{
struct kprobe *kp;
list_for_each_entry_rcu(kp, &p->list, list) {
if (kp->pre_handler && likely(!kprobe_disabled(kp))) {
set_kprobe_instance(kp);
if (kp->pre_handler(kp, regs))
return 1;
}
reset_kprobe_instance();
}
return 0;
}
3. 注销kprobe
注销入口,先执行上半部注销,等待RCU的GP结束后再调用下半部注销释放资源:
void unregister_kprobes(struct kprobe **kps, int num)
{
int i;
if (num <= 0)
return;
scoped_guard(mutex, &kprobe_mutex) {
for (i = 0; i < num; i++)
if (__unregister_kprobe_top(kps[i]) < 0)
kps[i]->addr = NULL;
}
synchronize_rcu();
for (i = 0; i < num; i++)
if (kps[i]->addr)
__unregister_kprobe_bottom(kps[i]);
}
3.1 注销上半部
static int __unregister_kprobe_top(struct kprobe *p)
{
struct kprobe *ap, *list_p;
// 设置 DISABLE flag,并且调用disarm_kprobe,将原来的指令恢复
ap = __disable_kprobe(p);
if (IS_ERR(ap))
return PTR_ERR(ap);
WARN_ON(ap != p && !kprobe_aggrprobe(ap));
// 非聚合 / 聚合kprobe中的最后一个并且已经disarmed
// 直接从HashTable中删除当前kprobe
if (ap == p ||
(list_is_singular(&ap->list) && kprobe_disarmed(ap))) {
/*
* !disarmed could be happen if the probe is under delayed
* unoptimizing.
*/
hlist_del_rcu(&ap->hlist);
return 0;
}
// 聚合kprobe并且当前kprobe有post_handler
// 如果其他的kprobe都没有post_handler,那么就不需要后处理了
if (p->post_handler && !kprobe_gone(p)) {
list_for_each_entry(list_p, &ap->list, list) {
if ((list_p != p) && (list_p->post_handler))
break;
}
/* No other probe has post_handler */
if (list_entry_is_head(list_p, &ap->list, list)) {
/*
* For the kprobe-on-ftrace case, we keep the
* post_handler setting to identify this aggrprobe
* armed with kprobe_ipmodify_ops.
*/
if (!kprobe_ftrace(ap))
ap->post_handler = NULL;
}
}
// 将当前节点从聚合kprobe中删除
list_del_rcu(&p->list);
if (!kprobe_disabled(ap) && !kprobes_all_disarmed)
optimize_kprobe(ap);
return 0;
}
3.2 注销下半部
static void __unregister_kprobe_bottom(struct kprobe *p)
{
struct kprobe *ap;
if (list_empty(&p->list))
/* This is an independent kprobe */
// 释放注册时占用的slot
arch_remove_kprobe(p);
else if (list_is_singular(&p->list)) {
/* This is the last child of an aggrprobe */
ap = list_entry(p->list.next, struct kprobe, list);
list_del(&p->list);
// 释放注册时占用的slot以及ap结构体
free_aggr_kprobe(ap);
}
/* Otherwise, do nothing. */
}
4. 注册kretprobe
4.1 准备工作
核心是调用register_kprobe在函数入口处挂载一个kprobe, pre_handler为pre_handler_kretprobe:
int register_kretprobe(struct kretprobe *rp)
{
int ret;
int i;
void *addr;
// 与kprobe一样,先从kallsym中查询符号地址
// 但是这里多了一步检查要求挂载点必须位于函数入口,即最终的offset == 0
ret = kprobe_on_func_entry(rp->kp.addr, rp->kp.symbol_name, rp->kp.offset);
if (ret)
return ret;
/* If only 'rp->kp.addr' is specified, check reregistering kprobes */
// 防止重复注册
if (rp->kp.addr && warn_kprobe_rereg(&rp->kp))
return -EINVAL;
// 防止更改敏感函数的返回路径
if (kretprobe_blacklist_size) {
addr = kprobe_addr(&rp->kp);
if (IS_ERR(addr))
return PTR_ERR(addr);
for (i = 0; kretprobe_blacklist[i].name != NULL; i++) {
if (kretprobe_blacklist[i].addr == addr)
return -EINVAL;
}
}
if (rp->data_size > KRETPROBE_MAX_DATA_SIZE)
return -E2BIG;
// 挂载handler为kretprobe的处理函数
rp->kp.pre_handler = pre_handler_kretprobe;
rp->kp.post_handler = NULL;
// 设置kretprobe instance数量的最大值
if (rp->maxactive <= 0)
rp->maxactive = max_t(unsigned int, 10, 2*num_possible_cpus());
// 使用RETHOOK(推荐,但ARM64下暂不支持)
#ifdef CONFIG_KRETPROBE_ON_RETHOOK
// 分配一个rethook
rp->rh = rethook_alloc((void *)rp, kretprobe_rethook_handler,
sizeof(struct kretprobe_instance) +
rp->data_size, rp->maxactive);
if (IS_ERR(rp->rh))
return PTR_ERR(rp->rh);
rp->nmissed = 0;
// 注册kprobe,挂载函数入口点
ret = register_kprobe(&rp->kp);
if (ret != 0) {
rethook_free(rp->rh);
rp->rh = NULL;
}
// 不使用RETHOOK
#else /* !CONFIG_KRETPROBE_ON_RETHOOK */
// 分配一个holder
rp->rph = kzalloc_obj(struct kretprobe_holder);
if (!rp->rph)
return -ENOMEM;
// 分配一个对象池并交给holder管理
if (objpool_init(&rp->rph->pool, rp->maxactive, rp->data_size +
sizeof(struct kretprobe_instance), GFP_KERNEL,
rp->rph, kretprobe_init_inst, kretprobe_fini_pool)) {
kfree(rp->rph);
rp->rph = NULL;
return -ENOMEM;
}
rcu_assign_pointer(rp->rph->rp, rp);
rp->nmissed = 0;
/* Establish function entry probe point */
ret = register_kprobe(&rp->kp);
if (ret != 0)
free_rp_inst(rp);
#endif
return ret;
}
4.2 处理流程
kernel_clone的函数入口已经被修改,来到(不使用rh):
static int pre_handler_kretprobe(struct kprobe *p, struct pt_regs *regs)
{
struct kretprobe *rp = container_of(p, struct kretprobe, kp);
struct kretprobe_holder *rph = rp->rph;
struct kretprobe_instance *ri;
// 拿到一个instance
ri = objpool_pop(&rph->pool);
if (!ri) {
rp->nmissed++;
return 0;
}
// 调用用户注册好的entry_handler
if (rp->entry_handler && rp->entry_handler(ri, regs)) {
objpool_push(ri, &rph->pool);
return 0;
}
// 挂载rp
arch_prepare_kretprobe(ri, regs);
// 将instance挂载到task_struct中
__llist_add(&ri->llist, ¤t->kretprobe_instances);
return 0;
}
然后开始正式处理:
void __kprobes arch_prepare_kretprobe(struct kretprobe_instance *ri,
struct pt_regs *regs)
{
// 拿到lr和fp
ri->ret_addr = (kprobe_opcode_t *)regs->regs[30];
ri->fp = (void *)regs->regs[29];
// 将lr修改为trampoline,进入BRK#0x007
regs->regs[30] = (long)&__kretprobe_trampoline;
}
SYM_CODE_START(__kretprobe_trampoline)
/*
* Trigger a breakpoint exception. The PC will be adjusted by
* kretprobe_brk_handler(), and no subsequent instructions will
* be executed from the trampoline.
*/
brk #KRETPROBES_BRK_IMM
ASM_BUG()
SYM_CODE_END(__kretprobe_trampoline)
经过同步中断分发和BRK分发后,最终来到:
int __kprobes
kretprobe_brk_handler(struct pt_regs *regs, unsigned long esr)
{
if (regs->pc != (unsigned long)__kretprobe_trampoline)
return DBG_HOOK_ERROR;
regs->pc = kretprobe_trampoline_handler(regs, (void *)regs->regs[29]);
return DBG_HOOK_HANDLED;
}
调用到这里:
unsigned long __kretprobe_trampoline_handler(struct pt_regs *regs,
void *frame_pointer)
{
struct kretprobe_instance *ri = NULL;
struct llist_node *first, *node = NULL;
kprobe_opcode_t *correct_ret_addr;
struct kretprobe *rp;
// task_struct.kretprobe_instances中保存了该进程内的所有rpi,这里查找到当前函数中最后一个rpi保存到node中,并且返回该rpi中保存的lr
correct_ret_addr = __kretprobe_find_ret_addr(current, &node);
if (!correct_ret_addr) {
pr_err("kretprobe: Return address not found, not execute handler. Maybe there is a bug in the kernel.\n");
BUG_ON(1);
}
// 将pt_regs->pc 修改为原来的lr
instruction_pointer_set(regs, (unsigned long)correct_ret_addr);
// 遍历所有rpi,执行回调
first = current->kretprobe_instances.first;
while (first) {
ri = container_of(first, struct kretprobe_instance, llist);
if (WARN_ON_ONCE(ri->fp != frame_pointer))
break;
rp = get_kretprobe(ri);
if (rp && rp->handler) {
struct kprobe *prev = kprobe_running();
__this_cpu_write(current_kprobe, &rp->kp);
ri->ret_addr = correct_ret_addr;
rp->handler(ri, regs);
__this_cpu_write(current_kprobe, prev);
}
if (first == node)
break;
first = first->next;
}
arch_kretprobe_fixup_return(regs, correct_ret_addr);
// 当前栈帧内的rpi断开链接,然后回收
first = current->kretprobe_instances.first;
current->kretprobe_instances.first = node->next;
node->next = NULL;
/* Recycle free instances. */
while (first) {
ri = container_of(first, struct kretprobe_instance, llist);
first = first->next;
recycle_rp_inst(ri);
}
return (unsigned long)correct_ret_addr;
}
随后BRK中断返回,进程拿到正确的pc继续执行;
5. 注销kretprobe
基本上就是在注销kprobe:
void unregister_kretprobes(struct kretprobe **rps, int num)
{
int i;
if (num <= 0)
return;
for (i = 0; i < num; i++) {
guard(mutex)(&kprobe_mutex);
if (__unregister_kprobe_top(&rps[i]->kp) < 0)
rps[i]->kp.addr = NULL;
#ifdef CONFIG_KRETPROBE_ON_RETHOOK
rethook_free(rps[i]->rh);
#else
rcu_assign_pointer(rps[i]->rph->rp, NULL);
#endif
}
synchronize_rcu();
for (i = 0; i < num; i++) {
if (rps[i]->kp.addr) {
__unregister_kprobe_bottom(&rps[i]->kp);
#ifndef CONFIG_KRETPROBE_ON_RETHOOK
free_rp_inst(rps[i]);
#endif
}
}
}