mirror of
				https://github.com/torvalds/linux.git
				synced 2025-11-04 02:30:34 +02:00 
			
		
		
		
	kprobes/x86: Fix the return address of multiple kretprobes
Fix the return address of subsequent kretprobes when multiple
kretprobes are set on the same function.
For example:
 # cd /sys/kernel/debug/tracing
 # echo "r:event1 sys_symlink" > kprobe_events
 # echo "r:event2 sys_symlink" >> kprobe_events
 # echo 1 > events/kprobes/enable
 # ln -s /tmp/foo /tmp/bar
(without this patch)
 # cat trace
              ln-897   [000] 20404.133727: event1: (kretprobe_trampoline+0x0/0x4c <- sys_symlink)
              ln-897   [000] 20404.133747: event2: (system_call_fastpath+0x16/0x1b <- sys_symlink)
(with this patch)
 # cat trace
              ln-740   [000] 13799.491076: event1: (system_call_fastpath+0x16/0x1b <- sys_symlink)
              ln-740   [000] 13799.491096: event2: (system_call_fastpath+0x16/0x1b <- sys_symlink)
Signed-off-by: KUMANO Syuhei <kumano.prog@gmail.com>
Reviewed-by: Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com>
Cc: Frederic Weisbecker <fweisbec@gmail.com>
Cc: Ananth N Mavinakayanahalli <ananth@in.ibm.com>
Cc: Peter Zijlstra <a.p.zijlstra@chello.nl>
Cc: YOSHIFUJI Hideaki <yoshfuji@linux-ipv6.org>
LKML-Reference: <1281853084.3254.11.camel@camp10-laptop>
Signed-off-by: Ingo Molnar <mingo@elte.hu>
			
			
This commit is contained in:
		
							parent
							
								
									f203688440
								
							
						
					
					
						commit
						737480a0d5
					
				
					 1 changed files with 27 additions and 8 deletions
				
			
		| 
						 | 
					@ -709,6 +709,7 @@ static __used __kprobes void *trampoline_handler(struct pt_regs *regs)
 | 
				
			||||||
	struct hlist_node *node, *tmp;
 | 
						struct hlist_node *node, *tmp;
 | 
				
			||||||
	unsigned long flags, orig_ret_address = 0;
 | 
						unsigned long flags, orig_ret_address = 0;
 | 
				
			||||||
	unsigned long trampoline_address = (unsigned long)&kretprobe_trampoline;
 | 
						unsigned long trampoline_address = (unsigned long)&kretprobe_trampoline;
 | 
				
			||||||
 | 
						kprobe_opcode_t *correct_ret_addr = NULL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	INIT_HLIST_HEAD(&empty_rp);
 | 
						INIT_HLIST_HEAD(&empty_rp);
 | 
				
			||||||
	kretprobe_hash_lock(current, &head, &flags);
 | 
						kretprobe_hash_lock(current, &head, &flags);
 | 
				
			||||||
| 
						 | 
					@ -740,15 +741,7 @@ static __used __kprobes void *trampoline_handler(struct pt_regs *regs)
 | 
				
			||||||
			/* another task is sharing our hash bucket */
 | 
								/* another task is sharing our hash bucket */
 | 
				
			||||||
			continue;
 | 
								continue;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if (ri->rp && ri->rp->handler) {
 | 
					 | 
				
			||||||
			__get_cpu_var(current_kprobe) = &ri->rp->kp;
 | 
					 | 
				
			||||||
			get_kprobe_ctlblk()->kprobe_status = KPROBE_HIT_ACTIVE;
 | 
					 | 
				
			||||||
			ri->rp->handler(ri, regs);
 | 
					 | 
				
			||||||
			__get_cpu_var(current_kprobe) = NULL;
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		orig_ret_address = (unsigned long)ri->ret_addr;
 | 
							orig_ret_address = (unsigned long)ri->ret_addr;
 | 
				
			||||||
		recycle_rp_inst(ri, &empty_rp);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if (orig_ret_address != trampoline_address)
 | 
							if (orig_ret_address != trampoline_address)
 | 
				
			||||||
			/*
 | 
								/*
 | 
				
			||||||
| 
						 | 
					@ -761,6 +754,32 @@ static __used __kprobes void *trampoline_handler(struct pt_regs *regs)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	kretprobe_assert(ri, orig_ret_address, trampoline_address);
 | 
						kretprobe_assert(ri, orig_ret_address, trampoline_address);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						correct_ret_addr = ri->ret_addr;
 | 
				
			||||||
 | 
						hlist_for_each_entry_safe(ri, node, tmp, head, hlist) {
 | 
				
			||||||
 | 
							if (ri->task != current)
 | 
				
			||||||
 | 
								/* another task is sharing our hash bucket */
 | 
				
			||||||
 | 
								continue;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							orig_ret_address = (unsigned long)ri->ret_addr;
 | 
				
			||||||
 | 
							if (ri->rp && ri->rp->handler) {
 | 
				
			||||||
 | 
								__get_cpu_var(current_kprobe) = &ri->rp->kp;
 | 
				
			||||||
 | 
								get_kprobe_ctlblk()->kprobe_status = KPROBE_HIT_ACTIVE;
 | 
				
			||||||
 | 
								ri->ret_addr = correct_ret_addr;
 | 
				
			||||||
 | 
								ri->rp->handler(ri, regs);
 | 
				
			||||||
 | 
								__get_cpu_var(current_kprobe) = NULL;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							recycle_rp_inst(ri, &empty_rp);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							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;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	kretprobe_hash_unlock(current, &flags);
 | 
						kretprobe_hash_unlock(current, &flags);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	hlist_for_each_entry_safe(ri, node, tmp, &empty_rp, hlist) {
 | 
						hlist_for_each_entry_safe(ri, node, tmp, &empty_rp, hlist) {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in a new issue