diff options
-rw-r--r-- | arch/x86_64/kernel/kprobes.c | 76 |
1 files changed, 62 insertions, 14 deletions
diff --git a/arch/x86_64/kernel/kprobes.c b/arch/x86_64/kernel/kprobes.c index 324bf57925a9..4e680f87a75f 100644 --- a/arch/x86_64/kernel/kprobes.c +++ b/arch/x86_64/kernel/kprobes.c | |||
@@ -45,12 +45,10 @@ | |||
45 | 45 | ||
46 | static DECLARE_MUTEX(kprobe_mutex); | 46 | static DECLARE_MUTEX(kprobe_mutex); |
47 | 47 | ||
48 | /* kprobe_status settings */ | ||
49 | #define KPROBE_HIT_ACTIVE 0x00000001 | ||
50 | #define KPROBE_HIT_SS 0x00000002 | ||
51 | |||
52 | static struct kprobe *current_kprobe; | 48 | static struct kprobe *current_kprobe; |
53 | static unsigned long kprobe_status, kprobe_old_rflags, kprobe_saved_rflags; | 49 | static unsigned long kprobe_status, kprobe_old_rflags, kprobe_saved_rflags; |
50 | static struct kprobe *kprobe_prev; | ||
51 | static unsigned long kprobe_status_prev, kprobe_old_rflags_prev, kprobe_saved_rflags_prev; | ||
54 | static struct pt_regs jprobe_saved_regs; | 52 | static struct pt_regs jprobe_saved_regs; |
55 | static long *jprobe_saved_rsp; | 53 | static long *jprobe_saved_rsp; |
56 | static kprobe_opcode_t *get_insn_slot(void); | 54 | static kprobe_opcode_t *get_insn_slot(void); |
@@ -240,6 +238,31 @@ void arch_remove_kprobe(struct kprobe *p) | |||
240 | down(&kprobe_mutex); | 238 | down(&kprobe_mutex); |
241 | } | 239 | } |
242 | 240 | ||
241 | static inline void save_previous_kprobe(void) | ||
242 | { | ||
243 | kprobe_prev = current_kprobe; | ||
244 | kprobe_status_prev = kprobe_status; | ||
245 | kprobe_old_rflags_prev = kprobe_old_rflags; | ||
246 | kprobe_saved_rflags_prev = kprobe_saved_rflags; | ||
247 | } | ||
248 | |||
249 | static inline void restore_previous_kprobe(void) | ||
250 | { | ||
251 | current_kprobe = kprobe_prev; | ||
252 | kprobe_status = kprobe_status_prev; | ||
253 | kprobe_old_rflags = kprobe_old_rflags_prev; | ||
254 | kprobe_saved_rflags = kprobe_saved_rflags_prev; | ||
255 | } | ||
256 | |||
257 | static inline void set_current_kprobe(struct kprobe *p, struct pt_regs *regs) | ||
258 | { | ||
259 | current_kprobe = p; | ||
260 | kprobe_saved_rflags = kprobe_old_rflags | ||
261 | = (regs->eflags & (TF_MASK | IF_MASK)); | ||
262 | if (is_IF_modifier(p->ainsn.insn)) | ||
263 | kprobe_saved_rflags &= ~IF_MASK; | ||
264 | } | ||
265 | |||
243 | static void prepare_singlestep(struct kprobe *p, struct pt_regs *regs) | 266 | static void prepare_singlestep(struct kprobe *p, struct pt_regs *regs) |
244 | { | 267 | { |
245 | regs->eflags |= TF_MASK; | 268 | regs->eflags |= TF_MASK; |
@@ -319,10 +342,30 @@ int kprobe_handler(struct pt_regs *regs) | |||
319 | regs->eflags |= kprobe_saved_rflags; | 342 | regs->eflags |= kprobe_saved_rflags; |
320 | unlock_kprobes(); | 343 | unlock_kprobes(); |
321 | goto no_kprobe; | 344 | goto no_kprobe; |
345 | } else if (kprobe_status == KPROBE_HIT_SSDONE) { | ||
346 | /* TODO: Provide re-entrancy from | ||
347 | * post_kprobes_handler() and avoid exception | ||
348 | * stack corruption while single-stepping on | ||
349 | * the instruction of the new probe. | ||
350 | */ | ||
351 | arch_disarm_kprobe(p); | ||
352 | regs->rip = (unsigned long)p->addr; | ||
353 | ret = 1; | ||
354 | } else { | ||
355 | /* We have reentered the kprobe_handler(), since | ||
356 | * another probe was hit while within the | ||
357 | * handler. We here save the original kprobe | ||
358 | * variables and just single step on instruction | ||
359 | * of the new probe without calling any user | ||
360 | * handlers. | ||
361 | */ | ||
362 | save_previous_kprobe(); | ||
363 | set_current_kprobe(p, regs); | ||
364 | p->nmissed++; | ||
365 | prepare_singlestep(p, regs); | ||
366 | kprobe_status = KPROBE_REENTER; | ||
367 | return 1; | ||
322 | } | 368 | } |
323 | arch_disarm_kprobe(p); | ||
324 | regs->rip = (unsigned long)p->addr; | ||
325 | ret = 1; | ||
326 | } else { | 369 | } else { |
327 | p = current_kprobe; | 370 | p = current_kprobe; |
328 | if (p->break_handler && p->break_handler(p, regs)) { | 371 | if (p->break_handler && p->break_handler(p, regs)) { |
@@ -352,11 +395,7 @@ int kprobe_handler(struct pt_regs *regs) | |||
352 | } | 395 | } |
353 | 396 | ||
354 | kprobe_status = KPROBE_HIT_ACTIVE; | 397 | kprobe_status = KPROBE_HIT_ACTIVE; |
355 | current_kprobe = p; | 398 | set_current_kprobe(p, regs); |
356 | kprobe_saved_rflags = kprobe_old_rflags | ||
357 | = (regs->eflags & (TF_MASK | IF_MASK)); | ||
358 | if (is_IF_modifier(p->ainsn.insn)) | ||
359 | kprobe_saved_rflags &= ~IF_MASK; | ||
360 | 399 | ||
361 | if (p->pre_handler && p->pre_handler(p, regs)) | 400 | if (p->pre_handler && p->pre_handler(p, regs)) |
362 | /* handler has already set things up, so skip ss setup */ | 401 | /* handler has already set things up, so skip ss setup */ |
@@ -506,14 +545,23 @@ int post_kprobe_handler(struct pt_regs *regs) | |||
506 | if (!kprobe_running()) | 545 | if (!kprobe_running()) |
507 | return 0; | 546 | return 0; |
508 | 547 | ||
509 | if (current_kprobe->post_handler) | 548 | if ((kprobe_status != KPROBE_REENTER) && current_kprobe->post_handler) { |
549 | kprobe_status = KPROBE_HIT_SSDONE; | ||
510 | current_kprobe->post_handler(current_kprobe, regs, 0); | 550 | current_kprobe->post_handler(current_kprobe, regs, 0); |
551 | } | ||
511 | 552 | ||
512 | if (current_kprobe->post_handler != trampoline_post_handler) | 553 | if (current_kprobe->post_handler != trampoline_post_handler) |
513 | resume_execution(current_kprobe, regs); | 554 | resume_execution(current_kprobe, regs); |
514 | regs->eflags |= kprobe_saved_rflags; | 555 | regs->eflags |= kprobe_saved_rflags; |
515 | 556 | ||
516 | unlock_kprobes(); | 557 | /* Restore the original saved kprobes variables and continue. */ |
558 | if (kprobe_status == KPROBE_REENTER) { | ||
559 | restore_previous_kprobe(); | ||
560 | goto out; | ||
561 | } else { | ||
562 | unlock_kprobes(); | ||
563 | } | ||
564 | out: | ||
517 | preempt_enable_no_resched(); | 565 | preempt_enable_no_resched(); |
518 | 566 | ||
519 | /* | 567 | /* |