aboutsummaryrefslogtreecommitdiffstats
path: root/arch
diff options
context:
space:
mode:
Diffstat (limited to 'arch')
-rw-r--r--arch/x86_64/kernel/kprobes.c76
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
46static DECLARE_MUTEX(kprobe_mutex); 46static DECLARE_MUTEX(kprobe_mutex);
47 47
48/* kprobe_status settings */
49#define KPROBE_HIT_ACTIVE 0x00000001
50#define KPROBE_HIT_SS 0x00000002
51
52static struct kprobe *current_kprobe; 48static struct kprobe *current_kprobe;
53static unsigned long kprobe_status, kprobe_old_rflags, kprobe_saved_rflags; 49static unsigned long kprobe_status, kprobe_old_rflags, kprobe_saved_rflags;
50static struct kprobe *kprobe_prev;
51static unsigned long kprobe_status_prev, kprobe_old_rflags_prev, kprobe_saved_rflags_prev;
54static struct pt_regs jprobe_saved_regs; 52static struct pt_regs jprobe_saved_regs;
55static long *jprobe_saved_rsp; 53static long *jprobe_saved_rsp;
56static kprobe_opcode_t *get_insn_slot(void); 54static 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
241static 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
249static 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
257static 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
243static void prepare_singlestep(struct kprobe *p, struct pt_regs *regs) 266static 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 }
564out:
517 preempt_enable_no_resched(); 565 preempt_enable_no_resched();
518 566
519 /* 567 /*