aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAnanth N Mavinakayanahalli <ananth@in.ibm.com>2005-06-27 18:17:01 -0400
committerLinus Torvalds <torvalds@ppc970.osdl.org>2005-06-27 18:23:52 -0400
commit9ec4b1f356b3bad928ae8e2aa9caebfa737d52df (patch)
tree24d27ffed66595a9d864448ec53200ca1745f62c
parentd3b8a1a8496c83bc4a3cc76505c29255af15572c (diff)
[PATCH] kprobes: fix single-step out of line - take2
Now that PPC64 has no-execute support, here is a second try to fix the single step out of line during kprobe execution. Kprobes on x86_64 already solved this problem by allocating an executable page and using it as the scratch area for stepping out of line. Reuse that. Signed-off-by: Ananth N Mavinakayanahalli <ananth@in.ibm.com> Signed-off-by: Andrew Morton <akpm@osdl.org> Signed-off-by: Linus Torvalds <torvalds@osdl.org>
-rw-r--r--arch/ppc64/kernel/kprobes.c26
-rw-r--r--arch/x86_64/kernel/kprobes.c113
-rw-r--r--include/asm-ia64/kprobes.h1
-rw-r--r--include/asm-ppc64/kprobes.h2
-rw-r--r--include/linux/kprobes.h2
-rw-r--r--kernel/kprobes.c101
6 files changed, 128 insertions, 117 deletions
diff --git a/arch/ppc64/kernel/kprobes.c b/arch/ppc64/kernel/kprobes.c
index 782ce3efa2c1..86cc5496db9f 100644
--- a/arch/ppc64/kernel/kprobes.c
+++ b/arch/ppc64/kernel/kprobes.c
@@ -36,6 +36,8 @@
36#include <asm/kdebug.h> 36#include <asm/kdebug.h>
37#include <asm/sstep.h> 37#include <asm/sstep.h>
38 38
39static DECLARE_MUTEX(kprobe_mutex);
40
39static struct kprobe *current_kprobe; 41static struct kprobe *current_kprobe;
40static unsigned long kprobe_status, kprobe_saved_msr; 42static unsigned long kprobe_status, kprobe_saved_msr;
41static struct kprobe *kprobe_prev; 43static struct kprobe *kprobe_prev;
@@ -54,6 +56,15 @@ int arch_prepare_kprobe(struct kprobe *p)
54 printk("Cannot register a kprobe on rfid or mtmsrd\n"); 56 printk("Cannot register a kprobe on rfid or mtmsrd\n");
55 ret = -EINVAL; 57 ret = -EINVAL;
56 } 58 }
59
60 /* insn must be on a special executable page on ppc64 */
61 if (!ret) {
62 up(&kprobe_mutex);
63 p->ainsn.insn = get_insn_slot();
64 down(&kprobe_mutex);
65 if (!p->ainsn.insn)
66 ret = -ENOMEM;
67 }
57 return ret; 68 return ret;
58} 69}
59 70
@@ -79,16 +90,22 @@ void arch_disarm_kprobe(struct kprobe *p)
79 90
80void arch_remove_kprobe(struct kprobe *p) 91void arch_remove_kprobe(struct kprobe *p)
81{ 92{
93 up(&kprobe_mutex);
94 free_insn_slot(p->ainsn.insn);
95 down(&kprobe_mutex);
82} 96}
83 97
84static inline void prepare_singlestep(struct kprobe *p, struct pt_regs *regs) 98static inline void prepare_singlestep(struct kprobe *p, struct pt_regs *regs)
85{ 99{
100 kprobe_opcode_t insn = *p->ainsn.insn;
101
86 regs->msr |= MSR_SE; 102 regs->msr |= MSR_SE;
87 /*single step inline if it a breakpoint instruction*/ 103
88 if (p->opcode == BREAKPOINT_INSTRUCTION) 104 /* single step inline if it is a trap variant */
105 if (IS_TW(insn) || IS_TD(insn) || IS_TWI(insn) || IS_TDI(insn))
89 regs->nip = (unsigned long)p->addr; 106 regs->nip = (unsigned long)p->addr;
90 else 107 else
91 regs->nip = (unsigned long)&p->ainsn.insn; 108 regs->nip = (unsigned long)p->ainsn.insn;
92} 109}
93 110
94static inline void save_previous_kprobe(void) 111static inline void save_previous_kprobe(void)
@@ -205,9 +222,10 @@ no_kprobe:
205static void resume_execution(struct kprobe *p, struct pt_regs *regs) 222static void resume_execution(struct kprobe *p, struct pt_regs *regs)
206{ 223{
207 int ret; 224 int ret;
225 unsigned int insn = *p->ainsn.insn;
208 226
209 regs->nip = (unsigned long)p->addr; 227 regs->nip = (unsigned long)p->addr;
210 ret = emulate_step(regs, p->ainsn.insn[0]); 228 ret = emulate_step(regs, insn);
211 if (ret == 0) 229 if (ret == 0)
212 regs->nip = (unsigned long)p->addr + 4; 230 regs->nip = (unsigned long)p->addr + 4;
213} 231}
diff --git a/arch/x86_64/kernel/kprobes.c b/arch/x86_64/kernel/kprobes.c
index 4e680f87a75f..6a1c88376bef 100644
--- a/arch/x86_64/kernel/kprobes.c
+++ b/arch/x86_64/kernel/kprobes.c
@@ -38,7 +38,7 @@
38#include <linux/string.h> 38#include <linux/string.h>
39#include <linux/slab.h> 39#include <linux/slab.h>
40#include <linux/preempt.h> 40#include <linux/preempt.h>
41#include <linux/moduleloader.h> 41
42#include <asm/cacheflush.h> 42#include <asm/cacheflush.h>
43#include <asm/pgtable.h> 43#include <asm/pgtable.h>
44#include <asm/kdebug.h> 44#include <asm/kdebug.h>
@@ -51,8 +51,6 @@ static struct kprobe *kprobe_prev;
51static unsigned long kprobe_status_prev, kprobe_old_rflags_prev, kprobe_saved_rflags_prev; 51static unsigned long kprobe_status_prev, kprobe_old_rflags_prev, kprobe_saved_rflags_prev;
52static struct pt_regs jprobe_saved_regs; 52static struct pt_regs jprobe_saved_regs;
53static long *jprobe_saved_rsp; 53static long *jprobe_saved_rsp;
54static kprobe_opcode_t *get_insn_slot(void);
55static void free_insn_slot(kprobe_opcode_t *slot);
56void jprobe_return_end(void); 54void jprobe_return_end(void);
57 55
58/* copy of the kernel stack at the probe fire time */ 56/* copy of the kernel stack at the probe fire time */
@@ -681,112 +679,3 @@ int longjmp_break_handler(struct kprobe *p, struct pt_regs *regs)
681 } 679 }
682 return 0; 680 return 0;
683} 681}
684
685/*
686 * kprobe->ainsn.insn points to the copy of the instruction to be single-stepped.
687 * By default on x86_64, pages we get from kmalloc or vmalloc are not
688 * executable. Single-stepping an instruction on such a page yields an
689 * oops. So instead of storing the instruction copies in their respective
690 * kprobe objects, we allocate a page, map it executable, and store all the
691 * instruction copies there. (We can allocate additional pages if somebody
692 * inserts a huge number of probes.) Each page can hold up to INSNS_PER_PAGE
693 * instruction slots, each of which is MAX_INSN_SIZE*sizeof(kprobe_opcode_t)
694 * bytes.
695 */
696#define INSNS_PER_PAGE (PAGE_SIZE/(MAX_INSN_SIZE*sizeof(kprobe_opcode_t)))
697struct kprobe_insn_page {
698 struct hlist_node hlist;
699 kprobe_opcode_t *insns; /* page of instruction slots */
700 char slot_used[INSNS_PER_PAGE];
701 int nused;
702};
703
704static struct hlist_head kprobe_insn_pages;
705
706/**
707 * get_insn_slot() - Find a slot on an executable page for an instruction.
708 * We allocate an executable page if there's no room on existing ones.
709 */
710static kprobe_opcode_t *get_insn_slot(void)
711{
712 struct kprobe_insn_page *kip;
713 struct hlist_node *pos;
714
715 hlist_for_each(pos, &kprobe_insn_pages) {
716 kip = hlist_entry(pos, struct kprobe_insn_page, hlist);
717 if (kip->nused < INSNS_PER_PAGE) {
718 int i;
719 for (i = 0; i < INSNS_PER_PAGE; i++) {
720 if (!kip->slot_used[i]) {
721 kip->slot_used[i] = 1;
722 kip->nused++;
723 return kip->insns + (i*MAX_INSN_SIZE);
724 }
725 }
726 /* Surprise! No unused slots. Fix kip->nused. */
727 kip->nused = INSNS_PER_PAGE;
728 }
729 }
730
731 /* All out of space. Need to allocate a new page. Use slot 0.*/
732 kip = kmalloc(sizeof(struct kprobe_insn_page), GFP_KERNEL);
733 if (!kip) {
734 return NULL;
735 }
736
737 /*
738 * For the %rip-relative displacement fixups to be doable, we
739 * need our instruction copy to be within +/- 2GB of any data it
740 * might access via %rip. That is, within 2GB of where the
741 * kernel image and loaded module images reside. So we allocate
742 * a page in the module loading area.
743 */
744 kip->insns = module_alloc(PAGE_SIZE);
745 if (!kip->insns) {
746 kfree(kip);
747 return NULL;
748 }
749 INIT_HLIST_NODE(&kip->hlist);
750 hlist_add_head(&kip->hlist, &kprobe_insn_pages);
751 memset(kip->slot_used, 0, INSNS_PER_PAGE);
752 kip->slot_used[0] = 1;
753 kip->nused = 1;
754 return kip->insns;
755}
756
757/**
758 * free_insn_slot() - Free instruction slot obtained from get_insn_slot().
759 */
760static void free_insn_slot(kprobe_opcode_t *slot)
761{
762 struct kprobe_insn_page *kip;
763 struct hlist_node *pos;
764
765 hlist_for_each(pos, &kprobe_insn_pages) {
766 kip = hlist_entry(pos, struct kprobe_insn_page, hlist);
767 if (kip->insns <= slot
768 && slot < kip->insns+(INSNS_PER_PAGE*MAX_INSN_SIZE)) {
769 int i = (slot - kip->insns) / MAX_INSN_SIZE;
770 kip->slot_used[i] = 0;
771 kip->nused--;
772 if (kip->nused == 0) {
773 /*
774 * Page is no longer in use. Free it unless
775 * it's the last one. We keep the last one
776 * so as not to have to set it up again the
777 * next time somebody inserts a probe.
778 */
779 hlist_del(&kip->hlist);
780 if (hlist_empty(&kprobe_insn_pages)) {
781 INIT_HLIST_NODE(&kip->hlist);
782 hlist_add_head(&kip->hlist,
783 &kprobe_insn_pages);
784 } else {
785 module_free(NULL, kip->insns);
786 kfree(kip);
787 }
788 }
789 return;
790 }
791 }
792}
diff --git a/include/asm-ia64/kprobes.h b/include/asm-ia64/kprobes.h
index 7b700035e36d..25d8b1edfcba 100644
--- a/include/asm-ia64/kprobes.h
+++ b/include/asm-ia64/kprobes.h
@@ -28,6 +28,7 @@
28#include <linux/ptrace.h> 28#include <linux/ptrace.h>
29#include <asm/break.h> 29#include <asm/break.h>
30 30
31#define MAX_INSN_SIZE 16
31#define BREAK_INST (long)(__IA64_BREAK_KPROBE << 6) 32#define BREAK_INST (long)(__IA64_BREAK_KPROBE << 6)
32 33
33typedef union cmp_inst { 34typedef union cmp_inst {
diff --git a/include/asm-ppc64/kprobes.h b/include/asm-ppc64/kprobes.h
index 19b468bed059..790cf7c52774 100644
--- a/include/asm-ppc64/kprobes.h
+++ b/include/asm-ppc64/kprobes.h
@@ -45,7 +45,7 @@ typedef unsigned int kprobe_opcode_t;
45/* Architecture specific copy of original instruction */ 45/* Architecture specific copy of original instruction */
46struct arch_specific_insn { 46struct arch_specific_insn {
47 /* copy of original instruction */ 47 /* copy of original instruction */
48 kprobe_opcode_t insn[MAX_INSN_SIZE]; 48 kprobe_opcode_t *insn;
49}; 49};
50 50
51#ifdef CONFIG_KPROBES 51#ifdef CONFIG_KPROBES
diff --git a/include/linux/kprobes.h b/include/linux/kprobes.h
index 5e1a7b0d7b3f..d304d4579856 100644
--- a/include/linux/kprobes.h
+++ b/include/linux/kprobes.h
@@ -177,6 +177,8 @@ extern void arch_arm_kprobe(struct kprobe *p);
177extern void arch_disarm_kprobe(struct kprobe *p); 177extern void arch_disarm_kprobe(struct kprobe *p);
178extern void arch_remove_kprobe(struct kprobe *p); 178extern void arch_remove_kprobe(struct kprobe *p);
179extern void show_registers(struct pt_regs *regs); 179extern void show_registers(struct pt_regs *regs);
180extern kprobe_opcode_t *get_insn_slot(void);
181extern void free_insn_slot(kprobe_opcode_t *slot);
180 182
181/* Get the kprobe at this addr (if any). Must have called lock_kprobes */ 183/* Get the kprobe at this addr (if any). Must have called lock_kprobes */
182struct kprobe *get_kprobe(void *addr); 184struct kprobe *get_kprobe(void *addr);
diff --git a/kernel/kprobes.c b/kernel/kprobes.c
index 334f37472c56..65242529a75f 100644
--- a/kernel/kprobes.c
+++ b/kernel/kprobes.c
@@ -36,6 +36,7 @@
36#include <linux/hash.h> 36#include <linux/hash.h>
37#include <linux/init.h> 37#include <linux/init.h>
38#include <linux/module.h> 38#include <linux/module.h>
39#include <linux/moduleloader.h>
39#include <asm/cacheflush.h> 40#include <asm/cacheflush.h>
40#include <asm/errno.h> 41#include <asm/errno.h>
41#include <asm/kdebug.h> 42#include <asm/kdebug.h>
@@ -50,6 +51,106 @@ unsigned int kprobe_cpu = NR_CPUS;
50static DEFINE_SPINLOCK(kprobe_lock); 51static DEFINE_SPINLOCK(kprobe_lock);
51static struct kprobe *curr_kprobe; 52static struct kprobe *curr_kprobe;
52 53
54/*
55 * kprobe->ainsn.insn points to the copy of the instruction to be
56 * single-stepped. x86_64, POWER4 and above have no-exec support and
57 * stepping on the instruction on a vmalloced/kmalloced/data page
58 * is a recipe for disaster
59 */
60#define INSNS_PER_PAGE (PAGE_SIZE/(MAX_INSN_SIZE * sizeof(kprobe_opcode_t)))
61
62struct kprobe_insn_page {
63 struct hlist_node hlist;
64 kprobe_opcode_t *insns; /* Page of instruction slots */
65 char slot_used[INSNS_PER_PAGE];
66 int nused;
67};
68
69static struct hlist_head kprobe_insn_pages;
70
71/**
72 * get_insn_slot() - Find a slot on an executable page for an instruction.
73 * We allocate an executable page if there's no room on existing ones.
74 */
75kprobe_opcode_t *get_insn_slot(void)
76{
77 struct kprobe_insn_page *kip;
78 struct hlist_node *pos;
79
80 hlist_for_each(pos, &kprobe_insn_pages) {
81 kip = hlist_entry(pos, struct kprobe_insn_page, hlist);
82 if (kip->nused < INSNS_PER_PAGE) {
83 int i;
84 for (i = 0; i < INSNS_PER_PAGE; i++) {
85 if (!kip->slot_used[i]) {
86 kip->slot_used[i] = 1;
87 kip->nused++;
88 return kip->insns + (i * MAX_INSN_SIZE);
89 }
90 }
91 /* Surprise! No unused slots. Fix kip->nused. */
92 kip->nused = INSNS_PER_PAGE;
93 }
94 }
95
96 /* All out of space. Need to allocate a new page. Use slot 0.*/
97 kip = kmalloc(sizeof(struct kprobe_insn_page), GFP_KERNEL);
98 if (!kip) {
99 return NULL;
100 }
101
102 /*
103 * Use module_alloc so this page is within +/- 2GB of where the
104 * kernel image and loaded module images reside. This is required
105 * so x86_64 can correctly handle the %rip-relative fixups.
106 */
107 kip->insns = module_alloc(PAGE_SIZE);
108 if (!kip->insns) {
109 kfree(kip);
110 return NULL;
111 }
112 INIT_HLIST_NODE(&kip->hlist);
113 hlist_add_head(&kip->hlist, &kprobe_insn_pages);
114 memset(kip->slot_used, 0, INSNS_PER_PAGE);
115 kip->slot_used[0] = 1;
116 kip->nused = 1;
117 return kip->insns;
118}
119
120void free_insn_slot(kprobe_opcode_t *slot)
121{
122 struct kprobe_insn_page *kip;
123 struct hlist_node *pos;
124
125 hlist_for_each(pos, &kprobe_insn_pages) {
126 kip = hlist_entry(pos, struct kprobe_insn_page, hlist);
127 if (kip->insns <= slot &&
128 slot < kip->insns + (INSNS_PER_PAGE * MAX_INSN_SIZE)) {
129 int i = (slot - kip->insns) / MAX_INSN_SIZE;
130 kip->slot_used[i] = 0;
131 kip->nused--;
132 if (kip->nused == 0) {
133 /*
134 * Page is no longer in use. Free it unless
135 * it's the last one. We keep the last one
136 * so as not to have to set it up again the
137 * next time somebody inserts a probe.
138 */
139 hlist_del(&kip->hlist);
140 if (hlist_empty(&kprobe_insn_pages)) {
141 INIT_HLIST_NODE(&kip->hlist);
142 hlist_add_head(&kip->hlist,
143 &kprobe_insn_pages);
144 } else {
145 module_free(NULL, kip->insns);
146 kfree(kip);
147 }
148 }
149 return;
150 }
151 }
152}
153
53/* Locks kprobe: irqs must be disabled */ 154/* Locks kprobe: irqs must be disabled */
54void lock_kprobes(void) 155void lock_kprobes(void)
55{ 156{