aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorOleg Nesterov <oleg@redhat.com>2014-04-06 12:11:02 -0400
committerOleg Nesterov <oleg@redhat.com>2014-04-17 15:58:23 -0400
commit8e89c0be171b1a9ed2ba67168733ca811bb45d5c (patch)
tree3c4713011a5bd4f5eb895a51b4658681c505027c
parentd241006354c550c7d22f304e2fdf90137fb8eaab (diff)
uprobes/x86: Emulate relative call's
See the previous "Emulate unconditional relative jmp's" which explains why we can not execute "jmp" out-of-line, the same applies to "call". Emulating of rip-relative call is trivial, we only need to additionally push the ret-address. If this fails, we execute this instruction out of line and this should trigger the trap, the probed application should die or the same insn will be restarted if a signal handler expands the stack. We do not even need ->post_xol() for this case. But there is a corner (and almost theoretical) case: another thread can expand the stack right before we execute this insn out of line. In this case it hit the same problem we are trying to solve. So we simply turn the probed insn into "call 1f; 1:" and add ->post_xol() which restores ->sp and restarts. Many thanks to Jonathan who finally found the standalone reproducer, otherwise I would never resolve the "random SIGSEGV's under systemtap" bug-report. Now that the problem is clear we can write the simplified test-case: void probe_func(void), callee(void); int failed = 1; asm ( ".text\n" ".align 4096\n" ".globl probe_func\n" "probe_func:\n" "call callee\n" "ret" ); /* * This assumes that: * * - &probe_func = 0x401000 + a_bit, aligned = 0x402000 * * - xol_vma->vm_start = TASK_SIZE_MAX - PAGE_SIZE = 0x7fffffffe000 * as xol_add_vma() asks; the 1st slot = 0x7fffffffe080 * * so we can target the non-canonical address from xol_vma using * the simple math below, 100 * 4096 is just the random offset */ asm (".org . + 0x800000000000 - 0x7fffffffe080 - 5 - 1 + 100 * 4096\n"); void callee(void) { failed = 0; } int main(void) { probe_func(); return failed; } It SIGSEGV's if you probe "probe_func" (although this is not very reliable, randomize_va_space/etc can change the placement of xol area). Note: as Denys Vlasenko pointed out, amd and intel treat "callw" (0x66 0xe8) differently. This patch relies on lib/insn.c and thus implements the intel's behaviour: 0x66 is simply ignored. Fortunately nothing sane should ever use this insn, so we postpone the fix until we decide what should we do; emulate or not, support or not, etc. Reported-by: Jonathan Lebon <jlebon@redhat.com> Signed-off-by: Oleg Nesterov <oleg@redhat.com> Reviewed-by: Jim Keniston <jkenisto@us.ibm.com>
-rw-r--r--arch/x86/include/asm/uprobes.h1
-rw-r--r--arch/x86/kernel/uprobes.c80
2 files changed, 71 insertions, 10 deletions
diff --git a/arch/x86/include/asm/uprobes.h b/arch/x86/include/asm/uprobes.h
index e9fd4d5537ed..93bee7b93854 100644
--- a/arch/x86/include/asm/uprobes.h
+++ b/arch/x86/include/asm/uprobes.h
@@ -51,6 +51,7 @@ struct arch_uprobe {
51 struct { 51 struct {
52 s32 offs; 52 s32 offs;
53 u8 ilen; 53 u8 ilen;
54 u8 opc1;
54 } branch; 55 } branch;
55 }; 56 };
56}; 57};
diff --git a/arch/x86/kernel/uprobes.c b/arch/x86/kernel/uprobes.c
index f3c4212f3819..0914435001f5 100644
--- a/arch/x86/kernel/uprobes.c
+++ b/arch/x86/kernel/uprobes.c
@@ -461,34 +461,97 @@ static struct uprobe_xol_ops default_xol_ops = {
461 .post_xol = default_post_xol_op, 461 .post_xol = default_post_xol_op,
462}; 462};
463 463
464static bool branch_is_call(struct arch_uprobe *auprobe)
465{
466 return auprobe->branch.opc1 == 0xe8;
467}
468
464static bool branch_emulate_op(struct arch_uprobe *auprobe, struct pt_regs *regs) 469static bool branch_emulate_op(struct arch_uprobe *auprobe, struct pt_regs *regs)
465{ 470{
466 regs->ip += auprobe->branch.ilen + auprobe->branch.offs; 471 unsigned long new_ip = regs->ip += auprobe->branch.ilen;
472
473 if (branch_is_call(auprobe)) {
474 unsigned long new_sp = regs->sp - sizeof_long();
475 /*
476 * If it fails we execute this (mangled, see the comment in
477 * branch_clear_offset) insn out-of-line. In the likely case
478 * this should trigger the trap, and the probed application
479 * should die or restart the same insn after it handles the
480 * signal, arch_uprobe_post_xol() won't be even called.
481 *
482 * But there is corner case, see the comment in ->post_xol().
483 */
484 if (copy_to_user((void __user *)new_sp, &new_ip, sizeof_long()))
485 return false;
486 regs->sp = new_sp;
487 }
488
489 regs->ip = new_ip + auprobe->branch.offs;
467 return true; 490 return true;
468} 491}
469 492
493static int branch_post_xol_op(struct arch_uprobe *auprobe, struct pt_regs *regs)
494{
495 BUG_ON(!branch_is_call(auprobe));
496 /*
497 * We can only get here if branch_emulate_op() failed to push the ret
498 * address _and_ another thread expanded our stack before the (mangled)
499 * "call" insn was executed out-of-line. Just restore ->sp and restart.
500 * We could also restore ->ip and try to call branch_emulate_op() again.
501 */
502 regs->sp += sizeof_long();
503 return -ERESTART;
504}
505
506static void branch_clear_offset(struct arch_uprobe *auprobe, struct insn *insn)
507{
508 /*
509 * Turn this insn into "call 1f; 1:", this is what we will execute
510 * out-of-line if ->emulate() fails. We only need this to generate
511 * a trap, so that the probed task receives the correct signal with
512 * the properly filled siginfo.
513 *
514 * But see the comment in ->post_xol(), in the unlikely case it can
515 * succeed. So we need to ensure that the new ->ip can not fall into
516 * the non-canonical area and trigger #GP.
517 *
518 * We could turn it into (say) "pushf", but then we would need to
519 * divorce ->insn[] and ->ixol[]. We need to preserve the 1st byte
520 * of ->insn[] for set_orig_insn().
521 */
522 memset(auprobe->insn + insn_offset_immediate(insn),
523 0, insn->immediate.nbytes);
524}
525
470static struct uprobe_xol_ops branch_xol_ops = { 526static struct uprobe_xol_ops branch_xol_ops = {
471 .emulate = branch_emulate_op, 527 .emulate = branch_emulate_op,
528 .post_xol = branch_post_xol_op,
472}; 529};
473 530
474/* Returns -ENOSYS if branch_xol_ops doesn't handle this insn */ 531/* Returns -ENOSYS if branch_xol_ops doesn't handle this insn */
475static int branch_setup_xol_ops(struct arch_uprobe *auprobe, struct insn *insn) 532static int branch_setup_xol_ops(struct arch_uprobe *auprobe, struct insn *insn)
476{ 533{
534 u8 opc1 = OPCODE1(insn);
535
536 /* has the side-effect of processing the entire instruction */
537 insn_get_length(insn);
538 if (WARN_ON_ONCE(!insn_complete(insn)))
539 return -ENOEXEC;
477 540
478 switch (OPCODE1(insn)) { 541 switch (opc1) {
479 case 0xeb: /* jmp 8 */ 542 case 0xeb: /* jmp 8 */
480 case 0xe9: /* jmp 32 */ 543 case 0xe9: /* jmp 32 */
481 case 0x90: /* prefix* + nop; same as jmp with .offs = 0 */ 544 case 0x90: /* prefix* + nop; same as jmp with .offs = 0 */
482 break; 545 break;
546
547 case 0xe8: /* call relative */
548 branch_clear_offset(auprobe, insn);
549 break;
483 default: 550 default:
484 return -ENOSYS; 551 return -ENOSYS;
485 } 552 }
486 553
487 /* has the side-effect of processing the entire instruction */ 554 auprobe->branch.opc1 = opc1;
488 insn_get_length(insn);
489 if (WARN_ON_ONCE(!insn_complete(insn)))
490 return -ENOEXEC;
491
492 auprobe->branch.ilen = insn->length; 555 auprobe->branch.ilen = insn->length;
493 auprobe->branch.offs = insn->immediate.value; 556 auprobe->branch.offs = insn->immediate.value;
494 557
@@ -532,9 +595,6 @@ int arch_uprobe_analyze_insn(struct arch_uprobe *auprobe, struct mm_struct *mm,
532 case 0xca: 595 case 0xca:
533 fix_ip = false; 596 fix_ip = false;
534 break; 597 break;
535 case 0xe8: /* call relative - Fix return addr */
536 fix_call = true;
537 break;
538 case 0x9a: /* call absolute - Fix return addr, not ip */ 598 case 0x9a: /* call absolute - Fix return addr, not ip */
539 fix_call = true; 599 fix_call = true;
540 fix_ip = false; 600 fix_ip = false;