Commit 567a9fd8 authored by Masami Hiramatsu's avatar Masami Hiramatsu Committed by Ingo Molnar

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: default avatarMasami Hiramatsu <masami.hiramatsu.pt@hitachi.com>
Cc: Ananth N Mavinakayanahalli <ananth@in.ibm.com>
LKML-Reference: <4C298A6E.8070609@hitachi.com>
Signed-off-by: default avatarIngo Molnar <mingo@elte.hu>
parent a1d0ce82
...@@ -126,16 +126,22 @@ static void __kprobes synthesize_reljump(void *from, void *to) ...@@ -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 * Skip the prefixes of the instruction.
* X86_32 always returns 0
*/ */
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 #ifdef CONFIG_X86_64
if ((*insn & 0xf0) == 0x40) if (inat_is_rex_prefix(attr))
return 1; insn++;
#endif #endif
return 0; return insn;
} }
/* /*
...@@ -272,6 +278,9 @@ static int __kprobes can_probe(unsigned long paddr) ...@@ -272,6 +278,9 @@ static int __kprobes can_probe(unsigned long paddr)
*/ */
static int __kprobes is_IF_modifier(kprobe_opcode_t *insn) static int __kprobes is_IF_modifier(kprobe_opcode_t *insn)
{ {
/* Skip prefixes */
insn = skip_prefixes(insn);
switch (*insn) { switch (*insn) {
case 0xfa: /* cli */ case 0xfa: /* cli */
case 0xfb: /* sti */ case 0xfb: /* sti */
...@@ -280,13 +289,6 @@ static int __kprobes is_IF_modifier(kprobe_opcode_t *insn) ...@@ -280,13 +289,6 @@ static int __kprobes is_IF_modifier(kprobe_opcode_t *insn)
return 1; 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; return 0;
} }
...@@ -803,9 +805,8 @@ static void __kprobes resume_execution(struct kprobe *p, ...@@ -803,9 +805,8 @@ static void __kprobes resume_execution(struct kprobe *p,
unsigned long orig_ip = (unsigned long)p->addr; unsigned long orig_ip = (unsigned long)p->addr;
kprobe_opcode_t *insn = p->ainsn.insn; kprobe_opcode_t *insn = p->ainsn.insn;
/*skip the REX prefix*/ /* Skip prefixes */
if (is_REX_prefix(insn)) insn = skip_prefixes(insn);
insn++;
regs->flags &= ~X86_EFLAGS_TF; regs->flags &= ~X86_EFLAGS_TF;
switch (*insn) { switch (*insn) {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment