| From: Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com> |
| Date: Tue, 29 Jun 2010 05:53:50 +0000 (+0900) |
| Subject: kprobes/x86: Fix kprobes to skip prefixes correctly |
| X-Git-Tag: v2.6.36-rc1~41^2~53 |
| X-Git-Url: http://git.kernel.org/?p=linux%2Fkernel%2Fgit%2Ftorvalds%2Flinux-2.6.git;a=commitdiff_plain;h=567a9fd86735ccdc897768ed2dacdd5e83a13509 |
| |
| kprobes/x86: Fix kprobes to skip prefixes correctly |
| |
| Fix resume_execution() and is_IF_modifier() to skip x86 |
| instruction prefixes correctly by using x86 instruction |
| attribute. |
| |
| Without this fix, resume_execution() can't handle instructions |
| which have non-REX prefixes (REX prefixes are skipped). This |
| will cause unexpected kernel panic by hitting bad address when a |
| kprobe hits on two-byte ret (e.g. "repz ret" generated for |
| Athlon/K8 optimization), because it just checks "repz" and can't |
| recognize the "ret" instruction. |
| |
| These prefixes can be found easily with x86 instruction |
| attribute. This patch introduces skip_prefixes() and uses it in |
| resume_execution() and is_IF_modifier() to skip prefixes. |
| |
| Signed-off-by: Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com> |
| Cc: Ananth N Mavinakayanahalli <ananth@in.ibm.com> |
| LKML-Reference: <4C298A6E.8070609@hitachi.com> |
| Signed-off-by: Ingo Molnar <mingo@elte.hu> |
| |
| |
| diff --git a/arch/x86/kernel/kprobes.c b/arch/x86/kernel/kprobes.c |
| index 345a4b1..175f85c 100644 |
| |
| |
| @@ -126,16 +126,22 @@ static void __kprobes synthesize_reljump(void *from, void *to) |
| } |
| |
| /* |
| - * Check for the REX prefix which can only exist on X86_64 |
| - * X86_32 always returns 0 |
| + * Skip the prefixes of the instruction. |
| */ |
| -static int __kprobes is_REX_prefix(kprobe_opcode_t *insn) |
| +static kprobe_opcode_t *__kprobes skip_prefixes(kprobe_opcode_t *insn) |
| { |
| + insn_attr_t attr; |
| + |
| + attr = inat_get_opcode_attribute((insn_byte_t)*insn); |
| + while (inat_is_legacy_prefix(attr)) { |
| + insn++; |
| + attr = inat_get_opcode_attribute((insn_byte_t)*insn); |
| + } |
| #ifdef CONFIG_X86_64 |
| - if ((*insn & 0xf0) == 0x40) |
| - return 1; |
| + if (inat_is_rex_prefix(attr)) |
| + insn++; |
| #endif |
| - return 0; |
| + return insn; |
| } |
| |
| /* |
| @@ -272,6 +278,9 @@ static int __kprobes can_probe(unsigned long paddr) |
| */ |
| static int __kprobes is_IF_modifier(kprobe_opcode_t *insn) |
| { |
| + /* Skip prefixes */ |
| + insn = skip_prefixes(insn); |
| + |
| switch (*insn) { |
| case 0xfa: /* cli */ |
| case 0xfb: /* sti */ |
| @@ -280,13 +289,6 @@ static int __kprobes is_IF_modifier(kprobe_opcode_t *insn) |
| return 1; |
| } |
| |
| - /* |
| - * on X86_64, 0x40-0x4f are REX prefixes so we need to look |
| - * at the next byte instead.. but of course not recurse infinitely |
| - */ |
| - if (is_REX_prefix(insn)) |
| - return is_IF_modifier(++insn); |
| - |
| return 0; |
| } |
| |
| @@ -803,9 +805,8 @@ static void __kprobes resume_execution(struct kprobe *p, |
| unsigned long orig_ip = (unsigned long)p->addr; |
| kprobe_opcode_t *insn = p->ainsn.insn; |
| |
| - /*skip the REX prefix*/ |
| - if (is_REX_prefix(insn)) |
| - insn++; |
| + /* Skip prefixes */ |
| + insn = skip_prefixes(insn); |
| |
| regs->flags &= ~X86_EFLAGS_TF; |
| switch (*insn) { |