Skip to content

Commit abd82e5

Browse files
mhiramatsuryasaimadhu
authored andcommitted
x86/kprobes: Do not decode opcode in resume_execution()
Currently, kprobes decodes the opcode right after single-stepping in resume_execution(). But the opcode was already decoded while preparing arch_specific_insn in arch_copy_kprobe(). Decode the opcode in arch_copy_kprobe() instead of in resume_execution() and set some flags which classify the opcode for the resuming process. [ bp: Massage commit message. ] Signed-off-by: Masami Hiramatsu <[email protected]> Signed-off-by: Borislav Petkov <[email protected]> Acked-by: Steven Rostedt (VMware) <[email protected]> Link: https://lkml.kernel.org/r/160830072561.349576.3014979564448023213.stgit@devnote2
1 parent e71ba94 commit abd82e5

File tree

2 files changed

+81
-98
lines changed

2 files changed

+81
-98
lines changed

arch/x86/include/asm/kprobes.h

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -58,14 +58,17 @@ struct arch_specific_insn {
5858
/* copy of the original instruction */
5959
kprobe_opcode_t *insn;
6060
/*
61-
* boostable = false: This instruction type is not boostable.
62-
* boostable = true: This instruction has been boosted: we have
61+
* boostable = 0: This instruction type is not boostable.
62+
* boostable = 1: This instruction has been boosted: we have
6363
* added a relative jump after the instruction copy in insn,
6464
* so no single-step and fixup are needed (unless there's
6565
* a post_handler).
6666
*/
67-
bool boostable;
68-
bool if_modifier;
67+
unsigned boostable:1;
68+
unsigned if_modifier:1;
69+
unsigned is_call:1;
70+
unsigned is_pushf:1;
71+
unsigned is_abs_ip:1;
6972
/* Number of bytes of text poked */
7073
int tp_len;
7174
};

arch/x86/kernel/kprobes/core.c

Lines changed: 74 additions & 94 deletions
Original file line numberDiff line numberDiff line change
@@ -132,26 +132,6 @@ void synthesize_relcall(void *dest, void *from, void *to)
132132
}
133133
NOKPROBE_SYMBOL(synthesize_relcall);
134134

135-
/*
136-
* Skip the prefixes of the instruction.
137-
*/
138-
static kprobe_opcode_t *skip_prefixes(kprobe_opcode_t *insn)
139-
{
140-
insn_attr_t attr;
141-
142-
attr = inat_get_opcode_attribute((insn_byte_t)*insn);
143-
while (inat_is_legacy_prefix(attr)) {
144-
insn++;
145-
attr = inat_get_opcode_attribute((insn_byte_t)*insn);
146-
}
147-
#ifdef CONFIG_X86_64
148-
if (inat_is_rex_prefix(attr))
149-
insn++;
150-
#endif
151-
return insn;
152-
}
153-
NOKPROBE_SYMBOL(skip_prefixes);
154-
155135
/*
156136
* Returns non-zero if INSN is boostable.
157137
* RIP relative instructions are adjusted at copying time in 64 bits mode
@@ -311,25 +291,6 @@ static int can_probe(unsigned long paddr)
311291
return (addr == paddr);
312292
}
313293

314-
/*
315-
* Returns non-zero if opcode modifies the interrupt flag.
316-
*/
317-
static int is_IF_modifier(kprobe_opcode_t *insn)
318-
{
319-
/* Skip prefixes */
320-
insn = skip_prefixes(insn);
321-
322-
switch (*insn) {
323-
case 0xfa: /* cli */
324-
case 0xfb: /* sti */
325-
case 0xcf: /* iret/iretd */
326-
case 0x9d: /* popf/popfd */
327-
return 1;
328-
}
329-
330-
return 0;
331-
}
332-
333294
/*
334295
* Copy an instruction with recovering modified instruction by kprobes
335296
* and adjust the displacement if the instruction uses the %rip-relative
@@ -411,9 +372,9 @@ static int prepare_boost(kprobe_opcode_t *buf, struct kprobe *p,
411372
synthesize_reljump(buf + len, p->ainsn.insn + len,
412373
p->addr + insn->length);
413374
len += JMP32_INSN_SIZE;
414-
p->ainsn.boostable = true;
375+
p->ainsn.boostable = 1;
415376
} else {
416-
p->ainsn.boostable = false;
377+
p->ainsn.boostable = 0;
417378
}
418379

419380
return len;
@@ -450,6 +411,67 @@ void free_insn_page(void *page)
450411
module_memfree(page);
451412
}
452413

414+
static void set_resume_flags(struct kprobe *p, struct insn *insn)
415+
{
416+
insn_byte_t opcode = insn->opcode.bytes[0];
417+
418+
switch (opcode) {
419+
case 0xfa: /* cli */
420+
case 0xfb: /* sti */
421+
case 0x9d: /* popf/popfd */
422+
/* Check whether the instruction modifies Interrupt Flag or not */
423+
p->ainsn.if_modifier = 1;
424+
break;
425+
case 0x9c: /* pushfl */
426+
p->ainsn.is_pushf = 1;
427+
break;
428+
case 0xcf: /* iret */
429+
p->ainsn.if_modifier = 1;
430+
fallthrough;
431+
case 0xc2: /* ret/lret */
432+
case 0xc3:
433+
case 0xca:
434+
case 0xcb:
435+
case 0xea: /* jmp absolute -- ip is correct */
436+
/* ip is already adjusted, no more changes required */
437+
p->ainsn.is_abs_ip = 1;
438+
/* Without resume jump, this is boostable */
439+
p->ainsn.boostable = 1;
440+
break;
441+
case 0xe8: /* call relative - Fix return addr */
442+
p->ainsn.is_call = 1;
443+
break;
444+
#ifdef CONFIG_X86_32
445+
case 0x9a: /* call absolute -- same as call absolute, indirect */
446+
p->ainsn.is_call = 1;
447+
p->ainsn.is_abs_ip = 1;
448+
break;
449+
#endif
450+
case 0xff:
451+
opcode = insn->opcode.bytes[1];
452+
if ((opcode & 0x30) == 0x10) {
453+
/*
454+
* call absolute, indirect
455+
* Fix return addr; ip is correct.
456+
* But this is not boostable
457+
*/
458+
p->ainsn.is_call = 1;
459+
p->ainsn.is_abs_ip = 1;
460+
break;
461+
} else if (((opcode & 0x31) == 0x20) ||
462+
((opcode & 0x31) == 0x21)) {
463+
/*
464+
* jmp near and far, absolute indirect
465+
* ip is correct.
466+
*/
467+
p->ainsn.is_abs_ip = 1;
468+
/* Without resume jump, this is boostable */
469+
p->ainsn.boostable = 1;
470+
}
471+
break;
472+
}
473+
}
474+
453475
static int arch_copy_kprobe(struct kprobe *p)
454476
{
455477
struct insn insn;
@@ -467,8 +489,8 @@ static int arch_copy_kprobe(struct kprobe *p)
467489
*/
468490
len = prepare_boost(buf, p, &insn);
469491

470-
/* Check whether the instruction modifies Interrupt Flag or not */
471-
p->ainsn.if_modifier = is_IF_modifier(buf);
492+
/* Analyze the opcode and set resume flags */
493+
set_resume_flags(p, &insn);
472494

473495
/* Also, displacement change doesn't affect the first byte */
474496
p->opcode = buf[0];
@@ -491,6 +513,9 @@ int arch_prepare_kprobe(struct kprobe *p)
491513

492514
if (!can_probe((unsigned long)p->addr))
493515
return -EILSEQ;
516+
517+
memset(&p->ainsn, 0, sizeof(p->ainsn));
518+
494519
/* insn: must be on special executable page on x86. */
495520
p->ainsn.insn = get_insn_slot();
496521
if (!p->ainsn.insn)
@@ -806,72 +831,27 @@ NOKPROBE_SYMBOL(trampoline_handler);
806831
* 2) If the single-stepped instruction was a call, the return address
807832
* that is atop the stack is the address following the copied instruction.
808833
* We need to make it the address following the original instruction.
809-
*
810-
* If this is the first time we've single-stepped the instruction at
811-
* this probepoint, and the instruction is boostable, boost it: add a
812-
* jump instruction after the copied instruction, that jumps to the next
813-
* instruction after the probepoint.
814834
*/
815835
static void resume_execution(struct kprobe *p, struct pt_regs *regs,
816836
struct kprobe_ctlblk *kcb)
817837
{
818838
unsigned long *tos = stack_addr(regs);
819839
unsigned long copy_ip = (unsigned long)p->ainsn.insn;
820840
unsigned long orig_ip = (unsigned long)p->addr;
821-
kprobe_opcode_t *insn = p->ainsn.insn;
822-
823-
/* Skip prefixes */
824-
insn = skip_prefixes(insn);
825841

826842
regs->flags &= ~X86_EFLAGS_TF;
827-
switch (*insn) {
828-
case 0x9c: /* pushfl */
843+
844+
/* Fixup the contents of top of stack */
845+
if (p->ainsn.is_pushf) {
829846
*tos &= ~(X86_EFLAGS_TF | X86_EFLAGS_IF);
830847
*tos |= kcb->kprobe_old_flags;
831-
break;
832-
case 0xc2: /* iret/ret/lret */
833-
case 0xc3:
834-
case 0xca:
835-
case 0xcb:
836-
case 0xcf:
837-
case 0xea: /* jmp absolute -- ip is correct */
838-
/* ip is already adjusted, no more changes required */
839-
p->ainsn.boostable = true;
840-
goto no_change;
841-
case 0xe8: /* call relative - Fix return addr */
848+
} else if (p->ainsn.is_call) {
842849
*tos = orig_ip + (*tos - copy_ip);
843-
break;
844-
#ifdef CONFIG_X86_32
845-
case 0x9a: /* call absolute -- same as call absolute, indirect */
846-
*tos = orig_ip + (*tos - copy_ip);
847-
goto no_change;
848-
#endif
849-
case 0xff:
850-
if ((insn[1] & 0x30) == 0x10) {
851-
/*
852-
* call absolute, indirect
853-
* Fix return addr; ip is correct.
854-
* But this is not boostable
855-
*/
856-
*tos = orig_ip + (*tos - copy_ip);
857-
goto no_change;
858-
} else if (((insn[1] & 0x31) == 0x20) ||
859-
((insn[1] & 0x31) == 0x21)) {
860-
/*
861-
* jmp near and far, absolute indirect
862-
* ip is correct. And this is boostable
863-
*/
864-
p->ainsn.boostable = true;
865-
goto no_change;
866-
}
867-
break;
868-
default:
869-
break;
870850
}
871851

872-
regs->ip += orig_ip - copy_ip;
852+
if (!p->ainsn.is_abs_ip)
853+
regs->ip += orig_ip - copy_ip;
873854

874-
no_change:
875855
restore_btf();
876856
}
877857
NOKPROBE_SYMBOL(resume_execution);

0 commit comments

Comments
 (0)