diff options
| author | Masami Hiramatsu <mhiramat@redhat.com> | 2007-12-18 12:05:58 -0500 |
|---|---|---|
| committer | Ingo Molnar <mingo@elte.hu> | 2007-12-18 12:05:58 -0500 |
| commit | 0b0122faf4833548072d23f3c3063c23bc289746 (patch) | |
| tree | 4c5183e0b92142bdfb0f5abf2cab58329e997cbc /arch/x86/kernel | |
| parent | 29b6cd794e73eea7600541d06288a09861ffecb0 (diff) | |
x86: kprobes bugfix
Kprobes for x86-64 may cause a kernel crash if it inserted on "iret"
instruction. "call absolute" is invalid on x86-64, so we don't need
treat it.
- Change the processing order as same as x86-32.
- Add "iret"(0xcf) case.
- Remove next_rip local variable.
Signed-off-by: Masami Hiramatsu <mhiramat@redhat.com>
Signed-off-by: Ingo Molnar <mingo@elte.hu>
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Diffstat (limited to 'arch/x86/kernel')
| -rw-r--r-- | arch/x86/kernel/kprobes_64.c | 41 |
1 files changed, 18 insertions, 23 deletions
diff --git a/arch/x86/kernel/kprobes_64.c b/arch/x86/kernel/kprobes_64.c index a575059fb4..5df19a9f92 100644 --- a/arch/x86/kernel/kprobes_64.c +++ b/arch/x86/kernel/kprobes_64.c | |||
| @@ -485,7 +485,6 @@ static void __kprobes resume_execution(struct kprobe *p, | |||
| 485 | struct pt_regs *regs, struct kprobe_ctlblk *kcb) | 485 | struct pt_regs *regs, struct kprobe_ctlblk *kcb) |
| 486 | { | 486 | { |
| 487 | unsigned long *tos = (unsigned long *)regs->rsp; | 487 | unsigned long *tos = (unsigned long *)regs->rsp; |
| 488 | unsigned long next_rip = 0; | ||
| 489 | unsigned long copy_rip = (unsigned long)p->ainsn.insn; | 488 | unsigned long copy_rip = (unsigned long)p->ainsn.insn; |
| 490 | unsigned long orig_rip = (unsigned long)p->addr; | 489 | unsigned long orig_rip = (unsigned long)p->addr; |
| 491 | kprobe_opcode_t *insn = p->ainsn.insn; | 490 | kprobe_opcode_t *insn = p->ainsn.insn; |
| @@ -494,46 +493,42 @@ static void __kprobes resume_execution(struct kprobe *p, | |||
| 494 | if (*insn >= 0x40 && *insn <= 0x4f) | 493 | if (*insn >= 0x40 && *insn <= 0x4f) |
| 495 | insn++; | 494 | insn++; |
| 496 | 495 | ||
| 496 | regs->eflags &= ~TF_MASK; | ||
| 497 | switch (*insn) { | 497 | switch (*insn) { |
| 498 | case 0x9c: /* pushfl */ | 498 | case 0x9c: /* pushfl */ |
| 499 | *tos &= ~(TF_MASK | IF_MASK); | 499 | *tos &= ~(TF_MASK | IF_MASK); |
| 500 | *tos |= kcb->kprobe_old_rflags; | 500 | *tos |= kcb->kprobe_old_rflags; |
| 501 | break; | 501 | break; |
| 502 | case 0xc3: /* ret/lret */ | 502 | case 0xc2: /* iret/ret/lret */ |
| 503 | case 0xcb: | 503 | case 0xc3: |
| 504 | case 0xc2: | ||
| 505 | case 0xca: | 504 | case 0xca: |
| 506 | regs->eflags &= ~TF_MASK; | 505 | case 0xcb: |
| 507 | /* rip is already adjusted, no more changes required*/ | 506 | case 0xcf: |
| 508 | return; | 507 | case 0xea: /* jmp absolute -- ip is correct */ |
| 509 | case 0xe8: /* call relative - Fix return addr */ | 508 | /* ip is already adjusted, no more changes required */ |
| 509 | goto no_change; | ||
| 510 | case 0xe8: /* call relative - Fix return addr */ | ||
| 510 | *tos = orig_rip + (*tos - copy_rip); | 511 | *tos = orig_rip + (*tos - copy_rip); |
| 511 | break; | 512 | break; |
| 512 | case 0xff: | 513 | case 0xff: |
| 513 | if ((insn[1] & 0x30) == 0x10) { | 514 | if ((insn[1] & 0x30) == 0x10) { |
| 514 | /* call absolute, indirect */ | 515 | /* call absolute, indirect */ |
| 515 | /* Fix return addr; rip is correct. */ | 516 | /* Fix return addr; ip is correct. */ |
| 516 | next_rip = regs->rip; | ||
| 517 | *tos = orig_rip + (*tos - copy_rip); | 517 | *tos = orig_rip + (*tos - copy_rip); |
| 518 | goto no_change; | ||
| 518 | } else if (((insn[1] & 0x31) == 0x20) || /* jmp near, absolute indirect */ | 519 | } else if (((insn[1] & 0x31) == 0x20) || /* jmp near, absolute indirect */ |
| 519 | ((insn[1] & 0x31) == 0x21)) { /* jmp far, absolute indirect */ | 520 | ((insn[1] & 0x31) == 0x21)) { /* jmp far, absolute indirect */ |
| 520 | /* rip is correct. */ | 521 | /* ip is correct. */ |
| 521 | next_rip = regs->rip; | 522 | goto no_change; |
| 522 | } | 523 | } |
| 523 | break; | ||
| 524 | case 0xea: /* jmp absolute -- rip is correct */ | ||
| 525 | next_rip = regs->rip; | ||
| 526 | break; | ||
| 527 | default: | 524 | default: |
| 528 | break; | 525 | break; |
| 529 | } | 526 | } |
| 530 | 527 | ||
| 531 | regs->eflags &= ~TF_MASK; | 528 | regs->rip = orig_rip + (regs->rip - copy_rip); |
| 532 | if (next_rip) { | 529 | no_change: |
| 533 | regs->rip = next_rip; | 530 | |
| 534 | } else { | 531 | return; |
| 535 | regs->rip = orig_rip + (regs->rip - copy_rip); | ||
| 536 | } | ||
| 537 | } | 532 | } |
| 538 | 533 | ||
| 539 | int __kprobes post_kprobe_handler(struct pt_regs *regs) | 534 | int __kprobes post_kprobe_handler(struct pt_regs *regs) |
