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 | |
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')
-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 a575059fb420..5df19a9f9239 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) |