diff options
author | Martin Schwidefsky <schwidefsky@de.ibm.com> | 2010-11-10 04:05:58 -0500 |
---|---|---|
committer | Martin Schwidefsky <sky@mschwide.boeblingen.de.ibm.com> | 2010-11-10 04:05:54 -0500 |
commit | 89480801a17a3069f45169d40b828c8e511aa005 (patch) | |
tree | b2318335080bede28fbe107de5a5b67cc74b4918 /arch/s390/kernel/kprobes.c | |
parent | adb45839817392102e659c19e5c19aa39530021f (diff) |
[S390] kprobes: Fix the return address of multiple kretprobes
Analog to git commit 737480a0d525dae13306296da08029dff545bc72
fix the return address of subsequent kretprobes when multiple
kretprobes are set on the same function.
Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
Diffstat (limited to 'arch/s390/kernel/kprobes.c')
-rw-r--r-- | arch/s390/kernel/kprobes.c | 29 |
1 files changed, 26 insertions, 3 deletions
diff --git a/arch/s390/kernel/kprobes.c b/arch/s390/kernel/kprobes.c index 70cf73bdba25..2564793ec2b6 100644 --- a/arch/s390/kernel/kprobes.c +++ b/arch/s390/kernel/kprobes.c | |||
@@ -349,6 +349,7 @@ static int __kprobes trampoline_probe_handler(struct kprobe *p, | |||
349 | struct hlist_node *node, *tmp; | 349 | struct hlist_node *node, *tmp; |
350 | unsigned long flags, orig_ret_address = 0; | 350 | unsigned long flags, orig_ret_address = 0; |
351 | unsigned long trampoline_address = (unsigned long)&kretprobe_trampoline; | 351 | unsigned long trampoline_address = (unsigned long)&kretprobe_trampoline; |
352 | kprobe_opcode_t *correct_ret_addr = NULL; | ||
352 | 353 | ||
353 | INIT_HLIST_HEAD(&empty_rp); | 354 | INIT_HLIST_HEAD(&empty_rp); |
354 | kretprobe_hash_lock(current, &head, &flags); | 355 | kretprobe_hash_lock(current, &head, &flags); |
@@ -371,10 +372,32 @@ static int __kprobes trampoline_probe_handler(struct kprobe *p, | |||
371 | /* another task is sharing our hash bucket */ | 372 | /* another task is sharing our hash bucket */ |
372 | continue; | 373 | continue; |
373 | 374 | ||
374 | if (ri->rp && ri->rp->handler) | 375 | orig_ret_address = (unsigned long)ri->ret_addr; |
375 | ri->rp->handler(ri, regs); | 376 | |
377 | if (orig_ret_address != trampoline_address) | ||
378 | /* | ||
379 | * This is the real return address. Any other | ||
380 | * instances associated with this task are for | ||
381 | * other calls deeper on the call stack | ||
382 | */ | ||
383 | break; | ||
384 | } | ||
385 | |||
386 | kretprobe_assert(ri, orig_ret_address, trampoline_address); | ||
387 | |||
388 | correct_ret_addr = ri->ret_addr; | ||
389 | hlist_for_each_entry_safe(ri, node, tmp, head, hlist) { | ||
390 | if (ri->task != current) | ||
391 | /* another task is sharing our hash bucket */ | ||
392 | continue; | ||
376 | 393 | ||
377 | orig_ret_address = (unsigned long)ri->ret_addr; | 394 | orig_ret_address = (unsigned long)ri->ret_addr; |
395 | |||
396 | if (ri->rp && ri->rp->handler) { | ||
397 | ri->ret_addr = correct_ret_addr; | ||
398 | ri->rp->handler(ri, regs); | ||
399 | } | ||
400 | |||
378 | recycle_rp_inst(ri, &empty_rp); | 401 | recycle_rp_inst(ri, &empty_rp); |
379 | 402 | ||
380 | if (orig_ret_address != trampoline_address) { | 403 | if (orig_ret_address != trampoline_address) { |
@@ -386,7 +409,7 @@ static int __kprobes trampoline_probe_handler(struct kprobe *p, | |||
386 | break; | 409 | break; |
387 | } | 410 | } |
388 | } | 411 | } |
389 | kretprobe_assert(ri, orig_ret_address, trampoline_address); | 412 | |
390 | regs->psw.addr = orig_ret_address | PSW_ADDR_AMODE; | 413 | regs->psw.addr = orig_ret_address | PSW_ADDR_AMODE; |
391 | 414 | ||
392 | reset_current_kprobe(); | 415 | reset_current_kprobe(); |