aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--arch/x86/kernel/uprobes.c57
1 files changed, 55 insertions, 2 deletions
diff --git a/arch/x86/kernel/uprobes.c b/arch/x86/kernel/uprobes.c
index 0914435001f5..0460d04f0acc 100644
--- a/arch/x86/kernel/uprobes.c
+++ b/arch/x86/kernel/uprobes.c
@@ -466,9 +466,58 @@ static bool branch_is_call(struct arch_uprobe *auprobe)
466 return auprobe->branch.opc1 == 0xe8; 466 return auprobe->branch.opc1 == 0xe8;
467} 467}
468 468
469#define CASE_COND \
470 COND(70, 71, XF(OF)) \
471 COND(72, 73, XF(CF)) \
472 COND(74, 75, XF(ZF)) \
473 COND(78, 79, XF(SF)) \
474 COND(7a, 7b, XF(PF)) \
475 COND(76, 77, XF(CF) || XF(ZF)) \
476 COND(7c, 7d, XF(SF) != XF(OF)) \
477 COND(7e, 7f, XF(ZF) || XF(SF) != XF(OF))
478
479#define COND(op_y, op_n, expr) \
480 case 0x ## op_y: DO((expr) != 0) \
481 case 0x ## op_n: DO((expr) == 0)
482
483#define XF(xf) (!!(flags & X86_EFLAGS_ ## xf))
484
485static bool is_cond_jmp_opcode(u8 opcode)
486{
487 switch (opcode) {
488 #define DO(expr) \
489 return true;
490 CASE_COND
491 #undef DO
492
493 default:
494 return false;
495 }
496}
497
498static bool check_jmp_cond(struct arch_uprobe *auprobe, struct pt_regs *regs)
499{
500 unsigned long flags = regs->flags;
501
502 switch (auprobe->branch.opc1) {
503 #define DO(expr) \
504 return expr;
505 CASE_COND
506 #undef DO
507
508 default: /* not a conditional jmp */
509 return true;
510 }
511}
512
513#undef XF
514#undef COND
515#undef CASE_COND
516
469static bool branch_emulate_op(struct arch_uprobe *auprobe, struct pt_regs *regs) 517static bool branch_emulate_op(struct arch_uprobe *auprobe, struct pt_regs *regs)
470{ 518{
471 unsigned long new_ip = regs->ip += auprobe->branch.ilen; 519 unsigned long new_ip = regs->ip += auprobe->branch.ilen;
520 unsigned long offs = (long)auprobe->branch.offs;
472 521
473 if (branch_is_call(auprobe)) { 522 if (branch_is_call(auprobe)) {
474 unsigned long new_sp = regs->sp - sizeof_long(); 523 unsigned long new_sp = regs->sp - sizeof_long();
@@ -484,9 +533,11 @@ static bool branch_emulate_op(struct arch_uprobe *auprobe, struct pt_regs *regs)
484 if (copy_to_user((void __user *)new_sp, &new_ip, sizeof_long())) 533 if (copy_to_user((void __user *)new_sp, &new_ip, sizeof_long()))
485 return false; 534 return false;
486 regs->sp = new_sp; 535 regs->sp = new_sp;
536 } else if (!check_jmp_cond(auprobe, regs)) {
537 offs = 0;
487 } 538 }
488 539
489 regs->ip = new_ip + auprobe->branch.offs; 540 regs->ip = new_ip + offs;
490 return true; 541 return true;
491} 542}
492 543
@@ -547,8 +598,10 @@ static int branch_setup_xol_ops(struct arch_uprobe *auprobe, struct insn *insn)
547 case 0xe8: /* call relative */ 598 case 0xe8: /* call relative */
548 branch_clear_offset(auprobe, insn); 599 branch_clear_offset(auprobe, insn);
549 break; 600 break;
601
550 default: 602 default:
551 return -ENOSYS; 603 if (!is_cond_jmp_opcode(opc1))
604 return -ENOSYS;
552 } 605 }
553 606
554 auprobe->branch.opc1 = opc1; 607 auprobe->branch.opc1 = opc1;