diff options
-rw-r--r-- | arch/i386/kernel/kprobes.c | 92 | ||||
-rw-r--r-- | include/asm-i386/kprobes.h | 6 |
2 files changed, 96 insertions, 2 deletions
diff --git a/arch/i386/kernel/kprobes.c b/arch/i386/kernel/kprobes.c index b40614f5afe2..137bf612141b 100644 --- a/arch/i386/kernel/kprobes.c +++ b/arch/i386/kernel/kprobes.c | |||
@@ -41,6 +41,49 @@ void jprobe_return_end(void); | |||
41 | DEFINE_PER_CPU(struct kprobe *, current_kprobe) = NULL; | 41 | DEFINE_PER_CPU(struct kprobe *, current_kprobe) = NULL; |
42 | DEFINE_PER_CPU(struct kprobe_ctlblk, kprobe_ctlblk); | 42 | DEFINE_PER_CPU(struct kprobe_ctlblk, kprobe_ctlblk); |
43 | 43 | ||
44 | /* insert a jmp code */ | ||
45 | static inline void set_jmp_op(void *from, void *to) | ||
46 | { | ||
47 | struct __arch_jmp_op { | ||
48 | char op; | ||
49 | long raddr; | ||
50 | } __attribute__((packed)) *jop; | ||
51 | jop = (struct __arch_jmp_op *)from; | ||
52 | jop->raddr = (long)(to) - ((long)(from) + 5); | ||
53 | jop->op = RELATIVEJUMP_INSTRUCTION; | ||
54 | } | ||
55 | |||
56 | /* | ||
57 | * returns non-zero if opcodes can be boosted. | ||
58 | */ | ||
59 | static inline int can_boost(kprobe_opcode_t opcode) | ||
60 | { | ||
61 | switch (opcode & 0xf0 ) { | ||
62 | case 0x70: | ||
63 | return 0; /* can't boost conditional jump */ | ||
64 | case 0x90: | ||
65 | /* can't boost call and pushf */ | ||
66 | return opcode != 0x9a && opcode != 0x9c; | ||
67 | case 0xc0: | ||
68 | /* can't boost undefined opcodes and soft-interruptions */ | ||
69 | return (0xc1 < opcode && opcode < 0xc6) || | ||
70 | (0xc7 < opcode && opcode < 0xcc) || opcode == 0xcf; | ||
71 | case 0xd0: | ||
72 | /* can boost AA* and XLAT */ | ||
73 | return (opcode == 0xd4 || opcode == 0xd5 || opcode == 0xd7); | ||
74 | case 0xe0: | ||
75 | /* can boost in/out and (may be) jmps */ | ||
76 | return (0xe3 < opcode && opcode != 0xe8); | ||
77 | case 0xf0: | ||
78 | /* clear and set flags can be boost */ | ||
79 | return (opcode == 0xf5 || (0xf7 < opcode && opcode < 0xfe)); | ||
80 | default: | ||
81 | /* currently, can't boost 2 bytes opcodes */ | ||
82 | return opcode != 0x0f; | ||
83 | } | ||
84 | } | ||
85 | |||
86 | |||
44 | /* | 87 | /* |
45 | * returns non-zero if opcode modifies the interrupt flag. | 88 | * returns non-zero if opcode modifies the interrupt flag. |
46 | */ | 89 | */ |
@@ -65,6 +108,11 @@ int __kprobes arch_prepare_kprobe(struct kprobe *p) | |||
65 | 108 | ||
66 | memcpy(p->ainsn.insn, p->addr, MAX_INSN_SIZE * sizeof(kprobe_opcode_t)); | 109 | memcpy(p->ainsn.insn, p->addr, MAX_INSN_SIZE * sizeof(kprobe_opcode_t)); |
67 | p->opcode = *p->addr; | 110 | p->opcode = *p->addr; |
111 | if (can_boost(p->opcode)) { | ||
112 | p->ainsn.boostable = 0; | ||
113 | } else { | ||
114 | p->ainsn.boostable = -1; | ||
115 | } | ||
68 | return 0; | 116 | return 0; |
69 | } | 117 | } |
70 | 118 | ||
@@ -158,6 +206,9 @@ static int __kprobes kprobe_handler(struct pt_regs *regs) | |||
158 | kprobe_opcode_t *addr = NULL; | 206 | kprobe_opcode_t *addr = NULL; |
159 | unsigned long *lp; | 207 | unsigned long *lp; |
160 | struct kprobe_ctlblk *kcb; | 208 | struct kprobe_ctlblk *kcb; |
209 | #ifdef CONFIG_PREEMPT | ||
210 | unsigned pre_preempt_count = preempt_count(); | ||
211 | #endif /* CONFIG_PREEMPT */ | ||
161 | 212 | ||
162 | /* | 213 | /* |
163 | * We don't want to be preempted for the entire | 214 | * We don't want to be preempted for the entire |
@@ -252,6 +303,21 @@ static int __kprobes kprobe_handler(struct pt_regs *regs) | |||
252 | /* handler has already set things up, so skip ss setup */ | 303 | /* handler has already set things up, so skip ss setup */ |
253 | return 1; | 304 | return 1; |
254 | 305 | ||
306 | if (p->ainsn.boostable == 1 && | ||
307 | #ifdef CONFIG_PREEMPT | ||
308 | !(pre_preempt_count) && /* | ||
309 | * This enables booster when the direct | ||
310 | * execution path aren't preempted. | ||
311 | */ | ||
312 | #endif /* CONFIG_PREEMPT */ | ||
313 | !p->post_handler && !p->break_handler ) { | ||
314 | /* Boost up -- we can execute copied instructions directly */ | ||
315 | reset_current_kprobe(); | ||
316 | regs->eip = (unsigned long)p->ainsn.insn; | ||
317 | preempt_enable_no_resched(); | ||
318 | return 1; | ||
319 | } | ||
320 | |||
255 | ss_probe: | 321 | ss_probe: |
256 | prepare_singlestep(p, regs); | 322 | prepare_singlestep(p, regs); |
257 | kcb->kprobe_status = KPROBE_HIT_SS; | 323 | kcb->kprobe_status = KPROBE_HIT_SS; |
@@ -357,6 +423,8 @@ int __kprobes trampoline_probe_handler(struct kprobe *p, struct pt_regs *regs) | |||
357 | * 2) If the single-stepped instruction was a call, the return address | 423 | * 2) If the single-stepped instruction was a call, the return address |
358 | * that is atop the stack is the address following the copied instruction. | 424 | * that is atop the stack is the address following the copied instruction. |
359 | * We need to make it the address following the original instruction. | 425 | * We need to make it the address following the original instruction. |
426 | * | ||
427 | * This function also checks instruction size for preparing direct execution. | ||
360 | */ | 428 | */ |
361 | static void __kprobes resume_execution(struct kprobe *p, | 429 | static void __kprobes resume_execution(struct kprobe *p, |
362 | struct pt_regs *regs, struct kprobe_ctlblk *kcb) | 430 | struct pt_regs *regs, struct kprobe_ctlblk *kcb) |
@@ -377,6 +445,7 @@ static void __kprobes resume_execution(struct kprobe *p, | |||
377 | case 0xca: | 445 | case 0xca: |
378 | case 0xea: /* jmp absolute -- eip is correct */ | 446 | case 0xea: /* jmp absolute -- eip is correct */ |
379 | /* eip is already adjusted, no more changes required */ | 447 | /* eip is already adjusted, no more changes required */ |
448 | p->ainsn.boostable = 1; | ||
380 | goto no_change; | 449 | goto no_change; |
381 | case 0xe8: /* call relative - Fix return addr */ | 450 | case 0xe8: /* call relative - Fix return addr */ |
382 | *tos = orig_eip + (*tos - copy_eip); | 451 | *tos = orig_eip + (*tos - copy_eip); |
@@ -384,18 +453,37 @@ static void __kprobes resume_execution(struct kprobe *p, | |||
384 | case 0xff: | 453 | case 0xff: |
385 | if ((p->ainsn.insn[1] & 0x30) == 0x10) { | 454 | if ((p->ainsn.insn[1] & 0x30) == 0x10) { |
386 | /* call absolute, indirect */ | 455 | /* call absolute, indirect */ |
387 | /* Fix return addr; eip is correct. */ | 456 | /* |
457 | * Fix return addr; eip is correct. | ||
458 | * But this is not boostable | ||
459 | */ | ||
388 | *tos = orig_eip + (*tos - copy_eip); | 460 | *tos = orig_eip + (*tos - copy_eip); |
389 | goto no_change; | 461 | goto no_change; |
390 | } else if (((p->ainsn.insn[1] & 0x31) == 0x20) || /* jmp near, absolute indirect */ | 462 | } else if (((p->ainsn.insn[1] & 0x31) == 0x20) || /* jmp near, absolute indirect */ |
391 | ((p->ainsn.insn[1] & 0x31) == 0x21)) { /* jmp far, absolute indirect */ | 463 | ((p->ainsn.insn[1] & 0x31) == 0x21)) { /* jmp far, absolute indirect */ |
392 | /* eip is correct. */ | 464 | /* eip is correct. And this is boostable */ |
465 | p->ainsn.boostable = 1; | ||
393 | goto no_change; | 466 | goto no_change; |
394 | } | 467 | } |
395 | default: | 468 | default: |
396 | break; | 469 | break; |
397 | } | 470 | } |
398 | 471 | ||
472 | if (p->ainsn.boostable == 0) { | ||
473 | if ((regs->eip > copy_eip) && | ||
474 | (regs->eip - copy_eip) + 5 < MAX_INSN_SIZE) { | ||
475 | /* | ||
476 | * These instructions can be executed directly if it | ||
477 | * jumps back to correct address. | ||
478 | */ | ||
479 | set_jmp_op((void *)regs->eip, | ||
480 | (void *)orig_eip + (regs->eip - copy_eip)); | ||
481 | p->ainsn.boostable = 1; | ||
482 | } else { | ||
483 | p->ainsn.boostable = -1; | ||
484 | } | ||
485 | } | ||
486 | |||
399 | regs->eip = orig_eip + (regs->eip - copy_eip); | 487 | regs->eip = orig_eip + (regs->eip - copy_eip); |
400 | 488 | ||
401 | no_change: | 489 | no_change: |
diff --git a/include/asm-i386/kprobes.h b/include/asm-i386/kprobes.h index a0d2d74a7dda..57d157c5cf89 100644 --- a/include/asm-i386/kprobes.h +++ b/include/asm-i386/kprobes.h | |||
@@ -34,6 +34,7 @@ struct pt_regs; | |||
34 | 34 | ||
35 | typedef u8 kprobe_opcode_t; | 35 | typedef u8 kprobe_opcode_t; |
36 | #define BREAKPOINT_INSTRUCTION 0xcc | 36 | #define BREAKPOINT_INSTRUCTION 0xcc |
37 | #define RELATIVEJUMP_INSTRUCTION 0xe9 | ||
37 | #define MAX_INSN_SIZE 16 | 38 | #define MAX_INSN_SIZE 16 |
38 | #define MAX_STACK_SIZE 64 | 39 | #define MAX_STACK_SIZE 64 |
39 | #define MIN_STACK_SIZE(ADDR) (((MAX_STACK_SIZE) < \ | 40 | #define MIN_STACK_SIZE(ADDR) (((MAX_STACK_SIZE) < \ |
@@ -51,6 +52,11 @@ void kretprobe_trampoline(void); | |||
51 | struct arch_specific_insn { | 52 | struct arch_specific_insn { |
52 | /* copy of the original instruction */ | 53 | /* copy of the original instruction */ |
53 | kprobe_opcode_t *insn; | 54 | kprobe_opcode_t *insn; |
55 | /* | ||
56 | * If this flag is not 0, this kprobe can be boost when its | ||
57 | * post_handler and break_handler is not set. | ||
58 | */ | ||
59 | int boostable; | ||
54 | }; | 60 | }; |
55 | 61 | ||
56 | struct prev_kprobe { | 62 | struct prev_kprobe { |