diff options
author | Oleg Nesterov <oleg@redhat.com> | 2014-04-03 14:52:19 -0400 |
---|---|---|
committer | Oleg Nesterov <oleg@redhat.com> | 2014-04-17 15:58:21 -0400 |
commit | 75f9ef0b7f1aae33b7be7ba8d9c23c8cb48c2212 (patch) | |
tree | 03457632d08201ad36fc5742fda54695b0073f31 /arch/x86/kernel/uprobes.c | |
parent | 014940bad8e46ca7bd0483f760f9cba60088a3d4 (diff) |
uprobes/x86: Teach arch_uprobe_post_xol() to restart if possible
SIGILL after the failed arch_uprobe_post_xol() should only be used as
a last resort, we should try to restart the probed insn if possible.
Currently only adjust_ret_addr() can fail, and this can only happen if
another thread unmapped our stack after we executed "call" out-of-line.
Most probably the application if buggy, but even in this case it can
have a handler for SIGSEGV/etc. And in theory it can be even correct
and do something non-trivial with its memory.
Of course we can't restart unconditionally, so arch_uprobe_post_xol()
does this only if ->post_xol() returns -ERESTART even if currently this
is the only possible error.
default_post_xol_op(UPROBE_FIX_CALL) can always restart, but as Jim
pointed out it should not forget to pop off the return address pushed
by this insn executed out-of-line.
Note: this is not "perfect", we do not want the extra handler_chain()
after restart, but I think this is the best solution we can realistically
do without too much uglifications.
Signed-off-by: Oleg Nesterov <oleg@redhat.com>
Reviewed-by: Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com>
Reviewed-by: Jim Keniston <jkenisto@us.ibm.com>
Diffstat (limited to 'arch/x86/kernel/uprobes.c')
-rw-r--r-- | arch/x86/kernel/uprobes.c | 20 |
1 files changed, 16 insertions, 4 deletions
diff --git a/arch/x86/kernel/uprobes.c b/arch/x86/kernel/uprobes.c index e72903eacd43..cdd6909affcb 100644 --- a/arch/x86/kernel/uprobes.c +++ b/arch/x86/kernel/uprobes.c | |||
@@ -443,16 +443,22 @@ static int default_post_xol_op(struct arch_uprobe *auprobe, struct pt_regs *regs | |||
443 | { | 443 | { |
444 | struct uprobe_task *utask = current->utask; | 444 | struct uprobe_task *utask = current->utask; |
445 | long correction = (long)(utask->vaddr - utask->xol_vaddr); | 445 | long correction = (long)(utask->vaddr - utask->xol_vaddr); |
446 | int ret = 0; | ||
447 | 446 | ||
448 | handle_riprel_post_xol(auprobe, regs, &correction); | 447 | handle_riprel_post_xol(auprobe, regs, &correction); |
449 | if (auprobe->fixups & UPROBE_FIX_IP) | 448 | if (auprobe->fixups & UPROBE_FIX_IP) |
450 | regs->ip += correction; | 449 | regs->ip += correction; |
451 | 450 | ||
452 | if (auprobe->fixups & UPROBE_FIX_CALL) | 451 | if (auprobe->fixups & UPROBE_FIX_CALL) { |
453 | ret = adjust_ret_addr(regs->sp, correction); | 452 | if (adjust_ret_addr(regs->sp, correction)) { |
453 | if (is_ia32_task()) | ||
454 | regs->sp += 4; | ||
455 | else | ||
456 | regs->sp += 8; | ||
457 | return -ERESTART; | ||
458 | } | ||
459 | } | ||
454 | 460 | ||
455 | return ret; | 461 | return 0; |
456 | } | 462 | } |
457 | 463 | ||
458 | static struct uprobe_xol_ops default_xol_ops = { | 464 | static struct uprobe_xol_ops default_xol_ops = { |
@@ -599,6 +605,12 @@ int arch_uprobe_post_xol(struct arch_uprobe *auprobe, struct pt_regs *regs) | |||
599 | int err = auprobe->ops->post_xol(auprobe, regs); | 605 | int err = auprobe->ops->post_xol(auprobe, regs); |
600 | if (err) { | 606 | if (err) { |
601 | arch_uprobe_abort_xol(auprobe, regs); | 607 | arch_uprobe_abort_xol(auprobe, regs); |
608 | /* | ||
609 | * Restart the probed insn. ->post_xol() must ensure | ||
610 | * this is really possible if it returns -ERESTART. | ||
611 | */ | ||
612 | if (err == -ERESTART) | ||
613 | return 0; | ||
602 | return err; | 614 | return err; |
603 | } | 615 | } |
604 | } | 616 | } |