forked from Minki/linux
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>
This commit is contained in:
parent
a1d0ce8213
commit
567a9fd867
@ -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) {
|
||||
|
Loading…
Reference in New Issue
Block a user