diff options
| -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 | } | ||
