diff options
| -rw-r--r-- | arch/x86/kernel/uprobes.c | 57 |
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 | |||
| 485 | static 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 | |||
| 498 | static 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 | |||
| 469 | static bool branch_emulate_op(struct arch_uprobe *auprobe, struct pt_regs *regs) | 517 | static 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; |
