kprobes.c

来自「LINUX 2.6.17.4的源码」· C语言 代码 · 共 706 行 · 第 1/2 页

C
706
字号
 */fastcall void *__kprobes trampoline_handler(struct pt_regs *regs){        struct kretprobe_instance *ri = NULL;        struct hlist_head *head;        struct hlist_node *node, *tmp;	unsigned long flags, orig_ret_address = 0;	unsigned long trampoline_address =(unsigned long)&kretprobe_trampoline;	spin_lock_irqsave(&kretprobe_lock, flags);        head = kretprobe_inst_table_head(current);	/*	 * It is possible to have multiple instances associated with a given	 * task either because an multiple functions in the call path	 * have a return probe installed on them, and/or more then one return	 * return probe was registered for a target function.	 *	 * We can handle this because:	 *     - instances are always inserted at the head of the list	 *     - when multiple return probes are registered for the same         *       function, the first instance's ret_addr will point to the	 *       real return address, and all the rest will point to	 *       kretprobe_trampoline	 */	hlist_for_each_entry_safe(ri, node, tmp, head, hlist) {                if (ri->task != current)			/* another task is sharing our hash bucket */                        continue;		if (ri->rp && ri->rp->handler){			__get_cpu_var(current_kprobe) = &ri->rp->kp;			ri->rp->handler(ri, regs);			__get_cpu_var(current_kprobe) = NULL;		}		orig_ret_address = (unsigned long)ri->ret_addr;		recycle_rp_inst(ri);		if (orig_ret_address != trampoline_address)			/*			 * This is the real return address. Any other			 * instances associated with this task are for			 * other calls deeper on the call stack			 */			break;	}	BUG_ON(!orig_ret_address || (orig_ret_address == trampoline_address));	spin_unlock_irqrestore(&kretprobe_lock, flags);	return (void*)orig_ret_address;}/* * Called after single-stepping.  p->addr is the address of the * instruction whose first byte has been replaced by the "int 3" * instruction.  To avoid the SMP problems that can occur when we * temporarily put back the original opcode to single-step, we * single-stepped a copy of the instruction.  The address of this * copy is p->ainsn.insn. * * This function prepares to return from the post-single-step * interrupt.  We have to fix up the stack as follows: * * 0) Except in the case of absolute or indirect jump or call instructions, * the new eip is relative to the copied instruction.  We need to make * it relative to the original instruction. * * 1) If the single-stepped instruction was pushfl, then the TF and IF * flags are set in the just-pushed eflags, and may need to be cleared. * * 2) If the single-stepped instruction was a call, the return address * that is atop the stack is the address following the copied instruction. * We need to make it the address following the original instruction. * * This function also checks instruction size for preparing direct execution. */static void __kprobes resume_execution(struct kprobe *p,		struct pt_regs *regs, struct kprobe_ctlblk *kcb){	unsigned long *tos = (unsigned long *)&regs->esp;	unsigned long copy_eip = (unsigned long)p->ainsn.insn;	unsigned long orig_eip = (unsigned long)p->addr;	regs->eflags &= ~TF_MASK;	switch (p->ainsn.insn[0]) {	case 0x9c:		/* pushfl */		*tos &= ~(TF_MASK | IF_MASK);		*tos |= kcb->kprobe_old_eflags;		break;	case 0xc2:		/* iret/ret/lret */	case 0xc3:	case 0xca:	case 0xcb:	case 0xcf:	case 0xea:		/* jmp absolute -- eip is correct */		/* eip is already adjusted, no more changes required */		p->ainsn.boostable = 1;		goto no_change;	case 0xe8:		/* call relative - Fix return addr */		*tos = orig_eip + (*tos - copy_eip);		break;	case 0x9a:		/* call absolute -- same as call absolute, indirect */		*tos = orig_eip + (*tos - copy_eip);		goto no_change;	case 0xff:		if ((p->ainsn.insn[1] & 0x30) == 0x10) {			/*			 * call absolute, indirect			 * Fix return addr; eip is correct.			 * But this is not boostable			 */			*tos = orig_eip + (*tos - copy_eip);			goto no_change;		} else if (((p->ainsn.insn[1] & 0x31) == 0x20) ||	/* jmp near, absolute indirect */			   ((p->ainsn.insn[1] & 0x31) == 0x21)) {	/* jmp far, absolute indirect */			/* eip is correct. And this is boostable */			p->ainsn.boostable = 1;			goto no_change;		}	default:		break;	}	if (p->ainsn.boostable == 0) {		if ((regs->eip > copy_eip) &&		    (regs->eip - copy_eip) + 5 < MAX_INSN_SIZE) {			/*			 * These instructions can be executed directly if it			 * jumps back to correct address.			 */			set_jmp_op((void *)regs->eip,				   (void *)orig_eip + (regs->eip - copy_eip));			p->ainsn.boostable = 1;		} else {			p->ainsn.boostable = -1;		}	}	regs->eip = orig_eip + (regs->eip - copy_eip);no_change:	return;}/* * Interrupts are disabled on entry as trap1 is an interrupt gate and they * remain disabled thoroughout this function. */static int __kprobes post_kprobe_handler(struct pt_regs *regs){	struct kprobe *cur = kprobe_running();	struct kprobe_ctlblk *kcb = get_kprobe_ctlblk();	if (!cur)		return 0;	if ((kcb->kprobe_status != KPROBE_REENTER) && cur->post_handler) {		kcb->kprobe_status = KPROBE_HIT_SSDONE;		cur->post_handler(cur, regs, 0);	}	resume_execution(cur, regs, kcb);	regs->eflags |= kcb->kprobe_saved_eflags;	/*Restore back the original saved kprobes variables and continue. */	if (kcb->kprobe_status == KPROBE_REENTER) {		restore_previous_kprobe(kcb);		goto out;	}	reset_current_kprobe();out:	preempt_enable_no_resched();	/*	 * if somebody else is singlestepping across a probe point, eflags	 * will have TF set, in which case, continue the remaining processing	 * of do_debug, as if this is not a probe hit.	 */	if (regs->eflags & TF_MASK)		return 0;	return 1;}static int __kprobes kprobe_fault_handler(struct pt_regs *regs, int trapnr){	struct kprobe *cur = kprobe_running();	struct kprobe_ctlblk *kcb = get_kprobe_ctlblk();	switch(kcb->kprobe_status) {	case KPROBE_HIT_SS:	case KPROBE_REENTER:		/*		 * We are here because the instruction being single		 * stepped caused a page fault. We reset the current		 * kprobe and the eip points back to the probe address		 * and allow the page fault handler to continue as a		 * normal page fault.		 */		regs->eip = (unsigned long)cur->addr;		regs->eflags |= kcb->kprobe_old_eflags;		if (kcb->kprobe_status == KPROBE_REENTER)			restore_previous_kprobe(kcb);		else			reset_current_kprobe();		preempt_enable_no_resched();		break;	case KPROBE_HIT_ACTIVE:	case KPROBE_HIT_SSDONE:		/*		 * We increment the nmissed count for accounting,		 * we can also use npre/npostfault count for accouting		 * these specific fault cases.		 */		kprobes_inc_nmissed_count(cur);		/*		 * We come here because instructions in the pre/post		 * handler caused the page_fault, this could happen		 * if handler tries to access user space by		 * copy_from_user(), get_user() etc. Let the		 * user-specified handler try to fix it first.		 */		if (cur->fault_handler && cur->fault_handler(cur, regs, trapnr))			return 1;		/*		 * In case the user-specified fault handler returned		 * zero, try to fix up.		 */		if (fixup_exception(regs))			return 1;		/*		 * fixup_exception() could not handle it,		 * Let do_page_fault() fix it.		 */		break;	default:		break;	}	return 0;}/* * Wrapper routine to for handling exceptions. */int __kprobes kprobe_exceptions_notify(struct notifier_block *self,				       unsigned long val, void *data){	struct die_args *args = (struct die_args *)data;	int ret = NOTIFY_DONE;	if (args->regs && user_mode(args->regs))		return ret;	switch (val) {	case DIE_INT3:		if (kprobe_handler(args->regs))			ret = NOTIFY_STOP;		break;	case DIE_DEBUG:		if (post_kprobe_handler(args->regs))			ret = NOTIFY_STOP;		break;	case DIE_GPF:	case DIE_PAGE_FAULT:		/* kprobe_running() needs smp_processor_id() */		preempt_disable();		if (kprobe_running() &&		    kprobe_fault_handler(args->regs, args->trapnr))			ret = NOTIFY_STOP;		preempt_enable();		break;	default:		break;	}	return ret;}int __kprobes setjmp_pre_handler(struct kprobe *p, struct pt_regs *regs){	struct jprobe *jp = container_of(p, struct jprobe, kp);	unsigned long addr;	struct kprobe_ctlblk *kcb = get_kprobe_ctlblk();	kcb->jprobe_saved_regs = *regs;	kcb->jprobe_saved_esp = &regs->esp;	addr = (unsigned long)(kcb->jprobe_saved_esp);	/*	 * TBD: As Linus pointed out, gcc assumes that the callee	 * owns the argument space and could overwrite it, e.g.	 * tailcall optimization. So, to be absolutely safe	 * we also save and restore enough stack bytes to cover	 * the argument area.	 */	memcpy(kcb->jprobes_stack, (kprobe_opcode_t *)addr,			MIN_STACK_SIZE(addr));	regs->eflags &= ~IF_MASK;	regs->eip = (unsigned long)(jp->entry);	return 1;}void __kprobes jprobe_return(void){	struct kprobe_ctlblk *kcb = get_kprobe_ctlblk();	asm volatile ("       xchgl   %%ebx,%%esp     \n"		      "       int3			\n"		      "       .globl jprobe_return_end	\n"		      "       jprobe_return_end:	\n"		      "       nop			\n"::"b"		      (kcb->jprobe_saved_esp):"memory");}int __kprobes longjmp_break_handler(struct kprobe *p, struct pt_regs *regs){	struct kprobe_ctlblk *kcb = get_kprobe_ctlblk();	u8 *addr = (u8 *) (regs->eip - 1);	unsigned long stack_addr = (unsigned long)(kcb->jprobe_saved_esp);	struct jprobe *jp = container_of(p, struct jprobe, kp);	if ((addr > (u8 *) jprobe_return) && (addr < (u8 *) jprobe_return_end)) {		if (&regs->esp != kcb->jprobe_saved_esp) {			struct pt_regs *saved_regs =			    container_of(kcb->jprobe_saved_esp,					    struct pt_regs, esp);			printk("current esp %p does not match saved esp %p\n",			       &regs->esp, kcb->jprobe_saved_esp);			printk("Saved registers for jprobe %p\n", jp);			show_registers(saved_regs);			printk("Current registers\n");			show_registers(regs);			BUG();		}		*regs = kcb->jprobe_saved_regs;		memcpy((kprobe_opcode_t *) stack_addr, kcb->jprobes_stack,		       MIN_STACK_SIZE(stack_addr));		preempt_enable_no_resched();		return 1;	}	return 0;}int __init arch_init_kprobes(void){	return 0;}

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?