diff options
Diffstat (limited to 'arch/i386/kernel/kprobes.c')
-rw-r--r-- | arch/i386/kernel/kprobes.c | 62 |
1 files changed, 49 insertions, 13 deletions
diff --git a/arch/i386/kernel/kprobes.c b/arch/i386/kernel/kprobes.c index b8e2bae0ab4f..3762f6b35ab2 100644 --- a/arch/i386/kernel/kprobes.c +++ b/arch/i386/kernel/kprobes.c | |||
@@ -37,12 +37,10 @@ | |||
37 | #include <asm/kdebug.h> | 37 | #include <asm/kdebug.h> |
38 | #include <asm/desc.h> | 38 | #include <asm/desc.h> |
39 | 39 | ||
40 | /* kprobe_status settings */ | ||
41 | #define KPROBE_HIT_ACTIVE 0x00000001 | ||
42 | #define KPROBE_HIT_SS 0x00000002 | ||
43 | |||
44 | static struct kprobe *current_kprobe; | 40 | static struct kprobe *current_kprobe; |
45 | static unsigned long kprobe_status, kprobe_old_eflags, kprobe_saved_eflags; | 41 | static unsigned long kprobe_status, kprobe_old_eflags, kprobe_saved_eflags; |
42 | static struct kprobe *kprobe_prev; | ||
43 | static unsigned long kprobe_status_prev, kprobe_old_eflags_prev, kprobe_saved_eflags_prev; | ||
46 | static struct pt_regs jprobe_saved_regs; | 44 | static struct pt_regs jprobe_saved_regs; |
47 | static long *jprobe_saved_esp; | 45 | static long *jprobe_saved_esp; |
48 | /* copy of the kernel stack at the probe fire time */ | 46 | /* copy of the kernel stack at the probe fire time */ |
@@ -93,6 +91,31 @@ void arch_remove_kprobe(struct kprobe *p) | |||
93 | { | 91 | { |
94 | } | 92 | } |
95 | 93 | ||
94 | static inline void save_previous_kprobe(void) | ||
95 | { | ||
96 | kprobe_prev = current_kprobe; | ||
97 | kprobe_status_prev = kprobe_status; | ||
98 | kprobe_old_eflags_prev = kprobe_old_eflags; | ||
99 | kprobe_saved_eflags_prev = kprobe_saved_eflags; | ||
100 | } | ||
101 | |||
102 | static inline void restore_previous_kprobe(void) | ||
103 | { | ||
104 | current_kprobe = kprobe_prev; | ||
105 | kprobe_status = kprobe_status_prev; | ||
106 | kprobe_old_eflags = kprobe_old_eflags_prev; | ||
107 | kprobe_saved_eflags = kprobe_saved_eflags_prev; | ||
108 | } | ||
109 | |||
110 | static inline void set_current_kprobe(struct kprobe *p, struct pt_regs *regs) | ||
111 | { | ||
112 | current_kprobe = p; | ||
113 | kprobe_saved_eflags = kprobe_old_eflags | ||
114 | = (regs->eflags & (TF_MASK | IF_MASK)); | ||
115 | if (is_IF_modifier(p->opcode)) | ||
116 | kprobe_saved_eflags &= ~IF_MASK; | ||
117 | } | ||
118 | |||
96 | static inline void prepare_singlestep(struct kprobe *p, struct pt_regs *regs) | 119 | static inline void prepare_singlestep(struct kprobe *p, struct pt_regs *regs) |
97 | { | 120 | { |
98 | regs->eflags |= TF_MASK; | 121 | regs->eflags |= TF_MASK; |
@@ -184,9 +207,18 @@ static int kprobe_handler(struct pt_regs *regs) | |||
184 | unlock_kprobes(); | 207 | unlock_kprobes(); |
185 | goto no_kprobe; | 208 | goto no_kprobe; |
186 | } | 209 | } |
187 | arch_disarm_kprobe(p); | 210 | /* We have reentered the kprobe_handler(), since |
188 | regs->eip = (unsigned long)p->addr; | 211 | * another probe was hit while within the handler. |
189 | ret = 1; | 212 | * We here save the original kprobes variables and |
213 | * just single step on the instruction of the new probe | ||
214 | * without calling any user handlers. | ||
215 | */ | ||
216 | save_previous_kprobe(); | ||
217 | set_current_kprobe(p, regs); | ||
218 | p->nmissed++; | ||
219 | prepare_singlestep(p, regs); | ||
220 | kprobe_status = KPROBE_REENTER; | ||
221 | return 1; | ||
190 | } else { | 222 | } else { |
191 | p = current_kprobe; | 223 | p = current_kprobe; |
192 | if (p->break_handler && p->break_handler(p, regs)) { | 224 | if (p->break_handler && p->break_handler(p, regs)) { |
@@ -221,11 +253,7 @@ static int kprobe_handler(struct pt_regs *regs) | |||
221 | } | 253 | } |
222 | 254 | ||
223 | kprobe_status = KPROBE_HIT_ACTIVE; | 255 | kprobe_status = KPROBE_HIT_ACTIVE; |
224 | current_kprobe = p; | 256 | set_current_kprobe(p, regs); |
225 | kprobe_saved_eflags = kprobe_old_eflags | ||
226 | = (regs->eflags & (TF_MASK | IF_MASK)); | ||
227 | if (is_IF_modifier(p->opcode)) | ||
228 | kprobe_saved_eflags &= ~IF_MASK; | ||
229 | 257 | ||
230 | if (p->pre_handler && p->pre_handler(p, regs)) | 258 | if (p->pre_handler && p->pre_handler(p, regs)) |
231 | /* handler has already set things up, so skip ss setup */ | 259 | /* handler has already set things up, so skip ss setup */ |
@@ -370,14 +398,22 @@ static inline int post_kprobe_handler(struct pt_regs *regs) | |||
370 | if (!kprobe_running()) | 398 | if (!kprobe_running()) |
371 | return 0; | 399 | return 0; |
372 | 400 | ||
373 | if (current_kprobe->post_handler) | 401 | if ((kprobe_status != KPROBE_REENTER) && current_kprobe->post_handler) { |
402 | kprobe_status = KPROBE_HIT_SSDONE; | ||
374 | current_kprobe->post_handler(current_kprobe, regs, 0); | 403 | current_kprobe->post_handler(current_kprobe, regs, 0); |
404 | } | ||
375 | 405 | ||
376 | if (current_kprobe->post_handler != trampoline_post_handler) | 406 | if (current_kprobe->post_handler != trampoline_post_handler) |
377 | resume_execution(current_kprobe, regs); | 407 | resume_execution(current_kprobe, regs); |
378 | regs->eflags |= kprobe_saved_eflags; | 408 | regs->eflags |= kprobe_saved_eflags; |
379 | 409 | ||
410 | /*Restore back the original saved kprobes variables and continue. */ | ||
411 | if (kprobe_status == KPROBE_REENTER) { | ||
412 | restore_previous_kprobe(); | ||
413 | goto out; | ||
414 | } | ||
380 | unlock_kprobes(); | 415 | unlock_kprobes(); |
416 | out: | ||
381 | preempt_enable_no_resched(); | 417 | preempt_enable_no_resched(); |
382 | 418 | ||
383 | /* | 419 | /* |