Skip to content
Stack & Ink
Go back

【Linux】 kprobe 源码分析

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_kprobekernel_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_handlerpre_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, &current->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
		}
	}
}

Share this post:

Previous Post
【Linux】 uprobe 源码分析
Next Post
【Linux】 RCU 源码分析