diff options
Diffstat (limited to 'kernel/kprobes.c')
-rw-r--r-- | kernel/kprobes.c | 53 |
1 files changed, 43 insertions, 10 deletions
diff --git a/kernel/kprobes.c b/kernel/kprobes.c index 3f57dfdc8f92..610c837ad9e0 100644 --- a/kernel/kprobes.c +++ b/kernel/kprobes.c | |||
@@ -37,6 +37,7 @@ | |||
37 | #include <linux/slab.h> | 37 | #include <linux/slab.h> |
38 | #include <linux/module.h> | 38 | #include <linux/module.h> |
39 | #include <linux/moduleloader.h> | 39 | #include <linux/moduleloader.h> |
40 | #include <linux/kallsyms.h> | ||
40 | #include <asm-generic/sections.h> | 41 | #include <asm-generic/sections.h> |
41 | #include <asm/cacheflush.h> | 42 | #include <asm/cacheflush.h> |
42 | #include <asm/errno.h> | 43 | #include <asm/errno.h> |
@@ -45,6 +46,16 @@ | |||
45 | #define KPROBE_HASH_BITS 6 | 46 | #define KPROBE_HASH_BITS 6 |
46 | #define KPROBE_TABLE_SIZE (1 << KPROBE_HASH_BITS) | 47 | #define KPROBE_TABLE_SIZE (1 << KPROBE_HASH_BITS) |
47 | 48 | ||
49 | |||
50 | /* | ||
51 | * Some oddball architectures like 64bit powerpc have function descriptors | ||
52 | * so this must be overridable. | ||
53 | */ | ||
54 | #ifndef kprobe_lookup_name | ||
55 | #define kprobe_lookup_name(name, addr) \ | ||
56 | addr = ((kprobe_opcode_t *)(kallsyms_lookup_name(name))) | ||
57 | #endif | ||
58 | |||
48 | static struct hlist_head kprobe_table[KPROBE_TABLE_SIZE]; | 59 | static struct hlist_head kprobe_table[KPROBE_TABLE_SIZE]; |
49 | static struct hlist_head kretprobe_inst_table[KPROBE_TABLE_SIZE]; | 60 | static struct hlist_head kretprobe_inst_table[KPROBE_TABLE_SIZE]; |
50 | static atomic_t kprobe_count; | 61 | static atomic_t kprobe_count; |
@@ -308,7 +319,8 @@ void __kprobes add_rp_inst(struct kretprobe_instance *ri) | |||
308 | } | 319 | } |
309 | 320 | ||
310 | /* Called with kretprobe_lock held */ | 321 | /* Called with kretprobe_lock held */ |
311 | void __kprobes recycle_rp_inst(struct kretprobe_instance *ri) | 322 | void __kprobes recycle_rp_inst(struct kretprobe_instance *ri, |
323 | struct hlist_head *head) | ||
312 | { | 324 | { |
313 | /* remove rp inst off the rprobe_inst_table */ | 325 | /* remove rp inst off the rprobe_inst_table */ |
314 | hlist_del(&ri->hlist); | 326 | hlist_del(&ri->hlist); |
@@ -320,7 +332,7 @@ void __kprobes recycle_rp_inst(struct kretprobe_instance *ri) | |||
320 | hlist_add_head(&ri->uflist, &ri->rp->free_instances); | 332 | hlist_add_head(&ri->uflist, &ri->rp->free_instances); |
321 | } else | 333 | } else |
322 | /* Unregistering */ | 334 | /* Unregistering */ |
323 | kfree(ri); | 335 | hlist_add_head(&ri->hlist, head); |
324 | } | 336 | } |
325 | 337 | ||
326 | struct hlist_head __kprobes *kretprobe_inst_table_head(struct task_struct *tsk) | 338 | struct hlist_head __kprobes *kretprobe_inst_table_head(struct task_struct *tsk) |
@@ -336,18 +348,24 @@ struct hlist_head __kprobes *kretprobe_inst_table_head(struct task_struct *tsk) | |||
336 | */ | 348 | */ |
337 | void __kprobes kprobe_flush_task(struct task_struct *tk) | 349 | void __kprobes kprobe_flush_task(struct task_struct *tk) |
338 | { | 350 | { |
339 | struct kretprobe_instance *ri; | 351 | struct kretprobe_instance *ri; |
340 | struct hlist_head *head; | 352 | struct hlist_head *head, empty_rp; |
341 | struct hlist_node *node, *tmp; | 353 | struct hlist_node *node, *tmp; |
342 | unsigned long flags = 0; | 354 | unsigned long flags = 0; |
343 | 355 | ||
356 | INIT_HLIST_HEAD(&empty_rp); | ||
344 | spin_lock_irqsave(&kretprobe_lock, flags); | 357 | spin_lock_irqsave(&kretprobe_lock, flags); |
345 | head = kretprobe_inst_table_head(tk); | 358 | head = kretprobe_inst_table_head(tk); |
346 | hlist_for_each_entry_safe(ri, node, tmp, head, hlist) { | 359 | hlist_for_each_entry_safe(ri, node, tmp, head, hlist) { |
347 | if (ri->task == tk) | 360 | if (ri->task == tk) |
348 | recycle_rp_inst(ri); | 361 | recycle_rp_inst(ri, &empty_rp); |
349 | } | 362 | } |
350 | spin_unlock_irqrestore(&kretprobe_lock, flags); | 363 | spin_unlock_irqrestore(&kretprobe_lock, flags); |
364 | |||
365 | hlist_for_each_entry_safe(ri, node, tmp, &empty_rp, hlist) { | ||
366 | hlist_del(&ri->hlist); | ||
367 | kfree(ri); | ||
368 | } | ||
351 | } | 369 | } |
352 | 370 | ||
353 | static inline void free_rp_inst(struct kretprobe *rp) | 371 | static inline void free_rp_inst(struct kretprobe *rp) |
@@ -447,6 +465,21 @@ static int __kprobes __register_kprobe(struct kprobe *p, | |||
447 | struct kprobe *old_p; | 465 | struct kprobe *old_p; |
448 | struct module *probed_mod; | 466 | struct module *probed_mod; |
449 | 467 | ||
468 | /* | ||
469 | * If we have a symbol_name argument look it up, | ||
470 | * and add it to the address. That way the addr | ||
471 | * field can either be global or relative to a symbol. | ||
472 | */ | ||
473 | if (p->symbol_name) { | ||
474 | if (p->addr) | ||
475 | return -EINVAL; | ||
476 | kprobe_lookup_name(p->symbol_name, p->addr); | ||
477 | } | ||
478 | |||
479 | if (!p->addr) | ||
480 | return -EINVAL; | ||
481 | p->addr = (kprobe_opcode_t *)(((char *)p->addr)+ p->offset); | ||
482 | |||
450 | if ((!kernel_text_address((unsigned long) p->addr)) || | 483 | if ((!kernel_text_address((unsigned long) p->addr)) || |
451 | in_kprobes_functions((unsigned long) p->addr)) | 484 | in_kprobes_functions((unsigned long) p->addr)) |
452 | return -EINVAL; | 485 | return -EINVAL; |
@@ -488,7 +521,7 @@ static int __kprobes __register_kprobe(struct kprobe *p, | |||
488 | (ARCH_INACTIVE_KPROBE_COUNT + 1)) | 521 | (ARCH_INACTIVE_KPROBE_COUNT + 1)) |
489 | register_page_fault_notifier(&kprobe_page_fault_nb); | 522 | register_page_fault_notifier(&kprobe_page_fault_nb); |
490 | 523 | ||
491 | arch_arm_kprobe(p); | 524 | arch_arm_kprobe(p); |
492 | 525 | ||
493 | out: | 526 | out: |
494 | mutex_unlock(&kprobe_mutex); | 527 | mutex_unlock(&kprobe_mutex); |