diff options
Diffstat (limited to 'arch/i386')
-rw-r--r-- | arch/i386/kernel/kprobes.c | 133 |
1 files changed, 70 insertions, 63 deletions
diff --git a/arch/i386/kernel/kprobes.c b/arch/i386/kernel/kprobes.c index 3762f6b35ab2..fc8b17521761 100644 --- a/arch/i386/kernel/kprobes.c +++ b/arch/i386/kernel/kprobes.c | |||
@@ -127,48 +127,23 @@ static inline void prepare_singlestep(struct kprobe *p, struct pt_regs *regs) | |||
127 | regs->eip = (unsigned long)&p->ainsn.insn; | 127 | regs->eip = (unsigned long)&p->ainsn.insn; |
128 | } | 128 | } |
129 | 129 | ||
130 | struct task_struct *arch_get_kprobe_task(void *ptr) | ||
131 | { | ||
132 | return ((struct thread_info *) (((unsigned long) ptr) & | ||
133 | (~(THREAD_SIZE -1))))->task; | ||
134 | } | ||
135 | |||
136 | void arch_prepare_kretprobe(struct kretprobe *rp, struct pt_regs *regs) | 130 | void arch_prepare_kretprobe(struct kretprobe *rp, struct pt_regs *regs) |
137 | { | 131 | { |
138 | unsigned long *sara = (unsigned long *)®s->esp; | 132 | unsigned long *sara = (unsigned long *)®s->esp; |
139 | struct kretprobe_instance *ri; | 133 | struct kretprobe_instance *ri; |
140 | static void *orig_ret_addr; | 134 | |
135 | if ((ri = get_free_rp_inst(rp)) != NULL) { | ||
136 | ri->rp = rp; | ||
137 | ri->task = current; | ||
138 | ri->ret_addr = (kprobe_opcode_t *) *sara; | ||
141 | 139 | ||
142 | /* | ||
143 | * Save the return address when the return probe hits | ||
144 | * the first time, and use it to populate the (krprobe | ||
145 | * instance)->ret_addr for subsequent return probes at | ||
146 | * the same addrress since stack address would have | ||
147 | * the kretprobe_trampoline by then. | ||
148 | */ | ||
149 | if (((void*) *sara) != kretprobe_trampoline) | ||
150 | orig_ret_addr = (void*) *sara; | ||
151 | |||
152 | if ((ri = get_free_rp_inst(rp)) != NULL) { | ||
153 | ri->rp = rp; | ||
154 | ri->stack_addr = sara; | ||
155 | ri->ret_addr = orig_ret_addr; | ||
156 | add_rp_inst(ri); | ||
157 | /* Replace the return addr with trampoline addr */ | 140 | /* Replace the return addr with trampoline addr */ |
158 | *sara = (unsigned long) &kretprobe_trampoline; | 141 | *sara = (unsigned long) &kretprobe_trampoline; |
159 | } else { | ||
160 | rp->nmissed++; | ||
161 | } | ||
162 | } | ||
163 | 142 | ||
164 | void arch_kprobe_flush_task(struct task_struct *tk) | 143 | add_rp_inst(ri); |
165 | { | 144 | } else { |
166 | struct kretprobe_instance *ri; | 145 | rp->nmissed++; |
167 | while ((ri = get_rp_inst_tsk(tk)) != NULL) { | 146 | } |
168 | *((unsigned long *)(ri->stack_addr)) = | ||
169 | (unsigned long) ri->ret_addr; | ||
170 | recycle_rp_inst(ri); | ||
171 | } | ||
172 | } | 147 | } |
173 | 148 | ||
174 | /* | 149 | /* |
@@ -286,36 +261,59 @@ no_kprobe: | |||
286 | */ | 261 | */ |
287 | int trampoline_probe_handler(struct kprobe *p, struct pt_regs *regs) | 262 | int trampoline_probe_handler(struct kprobe *p, struct pt_regs *regs) |
288 | { | 263 | { |
289 | struct task_struct *tsk; | 264 | struct kretprobe_instance *ri = NULL; |
290 | struct kretprobe_instance *ri; | 265 | struct hlist_head *head; |
291 | struct hlist_head *head; | 266 | struct hlist_node *node, *tmp; |
292 | struct hlist_node *node; | 267 | unsigned long orig_ret_address = 0; |
293 | unsigned long *sara = ((unsigned long *) ®s->esp) - 1; | 268 | unsigned long trampoline_address =(unsigned long)&kretprobe_trampoline; |
294 | |||
295 | tsk = arch_get_kprobe_task(sara); | ||
296 | head = kretprobe_inst_table_head(tsk); | ||
297 | |||
298 | hlist_for_each_entry(ri, node, head, hlist) { | ||
299 | if (ri->stack_addr == sara && ri->rp) { | ||
300 | if (ri->rp->handler) | ||
301 | ri->rp->handler(ri, regs); | ||
302 | } | ||
303 | } | ||
304 | return 0; | ||
305 | } | ||
306 | 269 | ||
307 | void trampoline_post_handler(struct kprobe *p, struct pt_regs *regs, | 270 | head = kretprobe_inst_table_head(current); |
308 | unsigned long flags) | ||
309 | { | ||
310 | struct kretprobe_instance *ri; | ||
311 | /* RA already popped */ | ||
312 | unsigned long *sara = ((unsigned long *)®s->esp) - 1; | ||
313 | 271 | ||
314 | while ((ri = get_rp_inst(sara))) { | 272 | /* |
315 | regs->eip = (unsigned long)ri->ret_addr; | 273 | * It is possible to have multiple instances associated with a given |
274 | * task either because an multiple functions in the call path | ||
275 | * have a return probe installed on them, and/or more then one return | ||
276 | * return probe was registered for a target function. | ||
277 | * | ||
278 | * We can handle this because: | ||
279 | * - instances are always inserted at the head of the list | ||
280 | * - when multiple return probes are registered for the same | ||
281 | * function, the first instance's ret_addr will point to the | ||
282 | * real return address, and all the rest will point to | ||
283 | * kretprobe_trampoline | ||
284 | */ | ||
285 | hlist_for_each_entry_safe(ri, node, tmp, head, hlist) { | ||
286 | if (ri->task != current) | ||
287 | /* another task is sharing our hash bucket */ | ||
288 | continue; | ||
289 | |||
290 | if (ri->rp && ri->rp->handler) | ||
291 | ri->rp->handler(ri, regs); | ||
292 | |||
293 | orig_ret_address = (unsigned long)ri->ret_addr; | ||
316 | recycle_rp_inst(ri); | 294 | recycle_rp_inst(ri); |
295 | |||
296 | if (orig_ret_address != trampoline_address) | ||
297 | /* | ||
298 | * This is the real return address. Any other | ||
299 | * instances associated with this task are for | ||
300 | * other calls deeper on the call stack | ||
301 | */ | ||
302 | break; | ||
317 | } | 303 | } |
318 | regs->eflags &= ~TF_MASK; | 304 | |
305 | BUG_ON(!orig_ret_address || (orig_ret_address == trampoline_address)); | ||
306 | regs->eip = orig_ret_address; | ||
307 | |||
308 | unlock_kprobes(); | ||
309 | preempt_enable_no_resched(); | ||
310 | |||
311 | /* | ||
312 | * By returning a non-zero value, we are telling | ||
313 | * kprobe_handler() that we have handled unlocking | ||
314 | * and re-enabling preemption. | ||
315 | */ | ||
316 | return 1; | ||
319 | } | 317 | } |
320 | 318 | ||
321 | /* | 319 | /* |
@@ -403,8 +401,7 @@ static inline int post_kprobe_handler(struct pt_regs *regs) | |||
403 | current_kprobe->post_handler(current_kprobe, regs, 0); | 401 | current_kprobe->post_handler(current_kprobe, regs, 0); |
404 | } | 402 | } |
405 | 403 | ||
406 | if (current_kprobe->post_handler != trampoline_post_handler) | 404 | resume_execution(current_kprobe, regs); |
407 | resume_execution(current_kprobe, regs); | ||
408 | regs->eflags |= kprobe_saved_eflags; | 405 | regs->eflags |= kprobe_saved_eflags; |
409 | 406 | ||
410 | /*Restore back the original saved kprobes variables and continue. */ | 407 | /*Restore back the original saved kprobes variables and continue. */ |
@@ -534,3 +531,13 @@ int longjmp_break_handler(struct kprobe *p, struct pt_regs *regs) | |||
534 | } | 531 | } |
535 | return 0; | 532 | return 0; |
536 | } | 533 | } |
534 | |||
535 | static struct kprobe trampoline_p = { | ||
536 | .addr = (kprobe_opcode_t *) &kretprobe_trampoline, | ||
537 | .pre_handler = trampoline_probe_handler | ||
538 | }; | ||
539 | |||
540 | int __init arch_init(void) | ||
541 | { | ||
542 | return register_kprobe(&trampoline_p); | ||
543 | } | ||