diff options
Diffstat (limited to 'arch/i386')
-rw-r--r-- | arch/i386/kernel/kprobes.c | 133 | ||||
-rw-r--r-- | arch/i386/kernel/process.c | 29 | ||||
-rw-r--r-- | arch/i386/kernel/syscall_table.S | 2 |
3 files changed, 101 insertions, 63 deletions
diff --git a/arch/i386/kernel/kprobes.c b/arch/i386/kernel/kprobes.c index 3762f6b35ab2..fc8b17521761 100644 --- a/arch/i386/kernel/kprobes.c +++ b/arch/i386/kernel/kprobes.c | |||
@@ -127,48 +127,23 @@ static inline void prepare_singlestep(struct kprobe *p, struct pt_regs *regs) | |||
127 | regs->eip = (unsigned long)&p->ainsn.insn; | 127 | regs->eip = (unsigned long)&p->ainsn.insn; |
128 | } | 128 | } |
129 | 129 | ||
130 | struct task_struct *arch_get_kprobe_task(void *ptr) | ||
131 | { | ||
132 | return ((struct thread_info *) (((unsigned long) ptr) & | ||
133 | (~(THREAD_SIZE -1))))->task; | ||
134 | } | ||
135 | |||
136 | void arch_prepare_kretprobe(struct kretprobe *rp, struct pt_regs *regs) | 130 | void arch_prepare_kretprobe(struct kretprobe *rp, struct pt_regs *regs) |
137 | { | 131 | { |
138 | unsigned long *sara = (unsigned long *)®s->esp; | 132 | unsigned long *sara = (unsigned long *)®s->esp; |
139 | struct kretprobe_instance *ri; | 133 | struct kretprobe_instance *ri; |
140 | static void *orig_ret_addr; | 134 | |
135 | if ((ri = get_free_rp_inst(rp)) != NULL) { | ||
136 | ri->rp = rp; | ||
137 | ri->task = current; | ||
138 | ri->ret_addr = (kprobe_opcode_t *) *sara; | ||
141 | 139 | ||
142 | /* | ||
143 | * Save the return address when the return probe hits | ||
144 | * the first time, and use it to populate the (krprobe | ||
145 | * instance)->ret_addr for subsequent return probes at | ||
146 | * the same addrress since stack address would have | ||
147 | * the kretprobe_trampoline by then. | ||
148 | */ | ||
149 | if (((void*) *sara) != kretprobe_trampoline) | ||
150 | orig_ret_addr = (void*) *sara; | ||
151 | |||
152 | if ((ri = get_free_rp_inst(rp)) != NULL) { | ||
153 | ri->rp = rp; | ||
154 | ri->stack_addr = sara; | ||
155 | ri->ret_addr = orig_ret_addr; | ||
156 | add_rp_inst(ri); | ||
157 | /* Replace the return addr with trampoline addr */ | 140 | /* Replace the return addr with trampoline addr */ |
158 | *sara = (unsigned long) &kretprobe_trampoline; | 141 | *sara = (unsigned long) &kretprobe_trampoline; |
159 | } else { | ||
160 | rp->nmissed++; | ||
161 | } | ||
162 | } | ||
163 | 142 | ||
164 | void arch_kprobe_flush_task(struct task_struct *tk) | 143 | add_rp_inst(ri); |
165 | { | 144 | } else { |
166 | struct kretprobe_instance *ri; | 145 | rp->nmissed++; |
167 | while ((ri = get_rp_inst_tsk(tk)) != NULL) { | 146 | } |
168 | *((unsigned long *)(ri->stack_addr)) = | ||
169 | (unsigned long) ri->ret_addr; | ||
170 | recycle_rp_inst(ri); | ||
171 | } | ||
172 | } | 147 | } |
173 | 148 | ||
174 | /* | 149 | /* |
@@ -286,36 +261,59 @@ no_kprobe: | |||
286 | */ | 261 | */ |
287 | int trampoline_probe_handler(struct kprobe *p, struct pt_regs *regs) | 262 | int trampoline_probe_handler(struct kprobe *p, struct pt_regs *regs) |
288 | { | 263 | { |
289 | struct task_struct *tsk; | 264 | struct kretprobe_instance *ri = NULL; |
290 | struct kretprobe_instance *ri; | 265 | struct hlist_head *head; |
291 | struct hlist_head *head; | 266 | struct hlist_node *node, *tmp; |
292 | struct hlist_node *node; | 267 | unsigned long orig_ret_address = 0; |
293 | unsigned long *sara = ((unsigned long *) ®s->esp) - 1; | 268 | unsigned long trampoline_address =(unsigned long)&kretprobe_trampoline; |
294 | |||
295 | tsk = arch_get_kprobe_task(sara); | ||
296 | head = kretprobe_inst_table_head(tsk); | ||
297 | |||
298 | hlist_for_each_entry(ri, node, head, hlist) { | ||
299 | if (ri->stack_addr == sara && ri->rp) { | ||
300 | if (ri->rp->handler) | ||
301 | ri->rp->handler(ri, regs); | ||
302 | } | ||
303 | } | ||
304 | return 0; | ||
305 | } | ||
306 | 269 | ||
307 | void trampoline_post_handler(struct kprobe *p, struct pt_regs *regs, | 270 | head = kretprobe_inst_table_head(current); |
308 | unsigned long flags) | ||
309 | { | ||
310 | struct kretprobe_instance *ri; | ||
311 | /* RA already popped */ | ||
312 | unsigned long *sara = ((unsigned long *)®s->esp) - 1; | ||
313 | 271 | ||
314 | while ((ri = get_rp_inst(sara))) { | 272 | /* |
315 | regs->eip = (unsigned long)ri->ret_addr; | 273 | * It is possible to have multiple instances associated with a given |
274 | * task either because an multiple functions in the call path | ||
275 | * have a return probe installed on them, and/or more then one return | ||
276 | * return probe was registered for a target function. | ||
277 | * | ||
278 | * We can handle this because: | ||
279 | * - instances are always inserted at the head of the list | ||
280 | * - when multiple return probes are registered for the same | ||
281 | * function, the first instance's ret_addr will point to the | ||
282 | * real return address, and all the rest will point to | ||
283 | * kretprobe_trampoline | ||
284 | */ | ||
285 | hlist_for_each_entry_safe(ri, node, tmp, head, hlist) { | ||
286 | if (ri->task != current) | ||
287 | /* another task is sharing our hash bucket */ | ||
288 | continue; | ||
289 | |||
290 | if (ri->rp && ri->rp->handler) | ||
291 | ri->rp->handler(ri, regs); | ||
292 | |||
293 | orig_ret_address = (unsigned long)ri->ret_addr; | ||
316 | recycle_rp_inst(ri); | 294 | recycle_rp_inst(ri); |
295 | |||
296 | if (orig_ret_address != trampoline_address) | ||
297 | /* | ||
298 | * This is the real return address. Any other | ||
299 | * instances associated with this task are for | ||
300 | * other calls deeper on the call stack | ||
301 | */ | ||
302 | break; | ||
317 | } | 303 | } |
318 | regs->eflags &= ~TF_MASK; | 304 | |
305 | BUG_ON(!orig_ret_address || (orig_ret_address == trampoline_address)); | ||
306 | regs->eip = orig_ret_address; | ||
307 | |||
308 | unlock_kprobes(); | ||
309 | preempt_enable_no_resched(); | ||
310 | |||
311 | /* | ||
312 | * By returning a non-zero value, we are telling | ||
313 | * kprobe_handler() that we have handled unlocking | ||
314 | * and re-enabling preemption. | ||
315 | */ | ||
316 | return 1; | ||
319 | } | 317 | } |
320 | 318 | ||
321 | /* | 319 | /* |
@@ -403,8 +401,7 @@ static inline int post_kprobe_handler(struct pt_regs *regs) | |||
403 | current_kprobe->post_handler(current_kprobe, regs, 0); | 401 | current_kprobe->post_handler(current_kprobe, regs, 0); |
404 | } | 402 | } |
405 | 403 | ||
406 | if (current_kprobe->post_handler != trampoline_post_handler) | 404 | resume_execution(current_kprobe, regs); |
407 | resume_execution(current_kprobe, regs); | ||
408 | regs->eflags |= kprobe_saved_eflags; | 405 | regs->eflags |= kprobe_saved_eflags; |
409 | 406 | ||
410 | /*Restore back the original saved kprobes variables and continue. */ | 407 | /*Restore back the original saved kprobes variables and continue. */ |
@@ -534,3 +531,13 @@ int longjmp_break_handler(struct kprobe *p, struct pt_regs *regs) | |||
534 | } | 531 | } |
535 | return 0; | 532 | return 0; |
536 | } | 533 | } |
534 | |||
535 | static struct kprobe trampoline_p = { | ||
536 | .addr = (kprobe_opcode_t *) &kretprobe_trampoline, | ||
537 | .pre_handler = trampoline_probe_handler | ||
538 | }; | ||
539 | |||
540 | int __init arch_init(void) | ||
541 | { | ||
542 | return register_kprobe(&trampoline_p); | ||
543 | } | ||
diff --git a/arch/i386/kernel/process.c b/arch/i386/kernel/process.c index 5f8cfa6b7940..ba243a4cc119 100644 --- a/arch/i386/kernel/process.c +++ b/arch/i386/kernel/process.c | |||
@@ -617,6 +617,33 @@ handle_io_bitmap(struct thread_struct *next, struct tss_struct *tss) | |||
617 | } | 617 | } |
618 | 618 | ||
619 | /* | 619 | /* |
620 | * This function selects if the context switch from prev to next | ||
621 | * has to tweak the TSC disable bit in the cr4. | ||
622 | */ | ||
623 | static inline void disable_tsc(struct task_struct *prev_p, | ||
624 | struct task_struct *next_p) | ||
625 | { | ||
626 | struct thread_info *prev, *next; | ||
627 | |||
628 | /* | ||
629 | * gcc should eliminate the ->thread_info dereference if | ||
630 | * has_secure_computing returns 0 at compile time (SECCOMP=n). | ||
631 | */ | ||
632 | prev = prev_p->thread_info; | ||
633 | next = next_p->thread_info; | ||
634 | |||
635 | if (has_secure_computing(prev) || has_secure_computing(next)) { | ||
636 | /* slow path here */ | ||
637 | if (has_secure_computing(prev) && | ||
638 | !has_secure_computing(next)) { | ||
639 | write_cr4(read_cr4() & ~X86_CR4_TSD); | ||
640 | } else if (!has_secure_computing(prev) && | ||
641 | has_secure_computing(next)) | ||
642 | write_cr4(read_cr4() | X86_CR4_TSD); | ||
643 | } | ||
644 | } | ||
645 | |||
646 | /* | ||
620 | * switch_to(x,yn) should switch tasks from x to y. | 647 | * switch_to(x,yn) should switch tasks from x to y. |
621 | * | 648 | * |
622 | * We fsave/fwait so that an exception goes off at the right time | 649 | * We fsave/fwait so that an exception goes off at the right time |
@@ -695,6 +722,8 @@ struct task_struct fastcall * __switch_to(struct task_struct *prev_p, struct tas | |||
695 | if (unlikely(prev->io_bitmap_ptr || next->io_bitmap_ptr)) | 722 | if (unlikely(prev->io_bitmap_ptr || next->io_bitmap_ptr)) |
696 | handle_io_bitmap(next, tss); | 723 | handle_io_bitmap(next, tss); |
697 | 724 | ||
725 | disable_tsc(prev_p, next_p); | ||
726 | |||
698 | return prev_p; | 727 | return prev_p; |
699 | } | 728 | } |
700 | 729 | ||
diff --git a/arch/i386/kernel/syscall_table.S b/arch/i386/kernel/syscall_table.S index 442a6e937b19..3db9a04aec6e 100644 --- a/arch/i386/kernel/syscall_table.S +++ b/arch/i386/kernel/syscall_table.S | |||
@@ -289,3 +289,5 @@ ENTRY(sys_call_table) | |||
289 | .long sys_add_key | 289 | .long sys_add_key |
290 | .long sys_request_key | 290 | .long sys_request_key |
291 | .long sys_keyctl | 291 | .long sys_keyctl |
292 | .long sys_ioprio_set | ||
293 | .long sys_ioprio_get /* 290 */ | ||