History

[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.

Ananth N Mavinakayanahalli committed 21 years ago

Commit 9ec4b1f1 parent d3b8a1a6 files · +128 −117Browse files

Unified · Split

6 files
  1. arch/ppc64/kernel/kprobes.c +22 −4
  2. arch/x86_64/kernel/kprobes.c +1 −112
  3. include/asm-ia64/kprobes.h +1 −0
  4. include/asm-ppc64/kprobes.h +1 −1
  5. include/linux/kprobes.h +2 −0
  6. kernel/kprobes.c +101 −0
arch/ppc64/kernel/kprobes.c+22−4
@@ -36,6 +36,8 @@ · 35 unmodified lines
3636 #include <asm/kdebug.h>
3737 #include <asm/sstep.h>
3838
39+static DECLARE_MUTEX(kprobe_mutex);
40+
3941 static struct kprobe *current_kprobe;
4042 static unsigned long kprobe_status, kprobe_saved_msr;
4143 static struct kprobe *kprobe_prev;
@@ -54,6 +56,15 @@ int arch_prepare_kprobe(struct kprobe *p) · 12 unmodified lines
5456 printk("Cannot register a kprobe on rfid or mtmsrd\n");
5557 ret = -EINVAL;
5658 }
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+ }
5768 return ret;
5869 }
5970
@@ -79,16 +90,22 @@ void arch_disarm_kprobe(struct kprobe *p) · 19 unmodified lines
7990
8091 void arch_remove_kprobe(struct kprobe *p)
8192 {
93+ up(&kprobe_mutex);
94+ free_insn_slot(p->ainsn.insn);
95+ down(&kprobe_mutex);
8296 }
8397
8498 static inline void prepare_singlestep(struct kprobe *p, struct pt_regs *regs)
8599 {
100+ kprobe_opcode_t insn = *p->ainsn.insn;
101+
86102 regs->msr |= MSR_SE;
87− /*single step inline if it a breakpoint instruction*/
88− if (p->opcode == BREAKPOINT_INSTRUCTION)
103+
104+ /* single step inline if it is a trap variant */
105+ if (IS_TW(insn) || IS_TD(insn) || IS_TWI(insn) || IS_TDI(insn))
89106 regs->nip = (unsigned long)p->addr;
90107 else
91− regs->nip = (unsigned long)&p->ainsn.insn;
108+ regs->nip = (unsigned long)p->ainsn.insn;
92109 }
93110
94111 static inline void save_previous_kprobe(void)
@@ -205,9 +222,10 @@ no_kprobe: · 110 unmodified lines
205222 static void resume_execution(struct kprobe *p, struct pt_regs *regs)
206223 {
207224 int ret;
225+ unsigned int insn = *p->ainsn.insn;
208226
209227 regs->nip = (unsigned long)p->addr;
210− ret = emulate_step(regs, p->ainsn.insn[0]);
228+ ret = emulate_step(regs, insn);
211229 if (ret == 0)
212230 regs->nip = (unsigned long)p->addr + 4;
213231 }
arch/x86_64/kernel/kprobes.c+1−112
@@ -38,7 +38,7 @@ · 37 unmodified lines
3838 #include <linux/string.h>
3939 #include <linux/slab.h>
4040 #include <linux/preempt.h>
41−#include <linux/moduleloader.h>
41+
4242 #include <asm/cacheflush.h>
4343 #include <asm/pgtable.h>
4444 #include <asm/kdebug.h>
@@ -51,8 +51,6 @@ static struct kprobe *kprobe_prev; · 6 unmodified lines
5151 static unsigned long kprobe_status_prev, kprobe_old_rflags_prev, kprobe_saved_rflags_prev;
5252 static struct pt_regs jprobe_saved_regs;
5353 static long *jprobe_saved_rsp;
54−static kprobe_opcode_t *get_insn_slot(void);
55−static void free_insn_slot(kprobe_opcode_t *slot);
5654 void jprobe_return_end(void);
5755
5856 /* 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) · 622 unmodified lines
681679 }
682680 return 0;
683681 }
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)))
697−struct 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−
704−static 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− */
710−static 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− */
760−static 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−}
include/asm-ia64/kprobes.h+1−0
@@ -28,6 +28,7 @@ · 27 unmodified lines
2828 #include <linux/ptrace.h>
2929 #include <asm/break.h>
3030
31+#define MAX_INSN_SIZE 16
3132 #define BREAK_INST (long)(__IA64_BREAK_KPROBE << 6)
3233
3334 typedef union cmp_inst {
include/asm-ppc64/kprobes.h+1−1
@@ -45,7 +45,7 @@ typedef unsigned int kprobe_opcode_t; · 44 unmodified lines
4545 /* Architecture specific copy of original instruction */
4646 struct arch_specific_insn {
4747 /* copy of original instruction */
48− kprobe_opcode_t insn[MAX_INSN_SIZE];
48+ kprobe_opcode_t *insn;
4949 };
5050
5151 #ifdef CONFIG_KPROBES
include/linux/kprobes.h+2−0
@@ -177,6 +177,8 @@ extern void arch_arm_kprobe(struct kprobe *p); · 176 unmodified lines
177177 extern void arch_disarm_kprobe(struct kprobe *p);
178178 extern void arch_remove_kprobe(struct kprobe *p);
179179 extern void show_registers(struct pt_regs *regs);
180+extern kprobe_opcode_t *get_insn_slot(void);
181+extern void free_insn_slot(kprobe_opcode_t *slot);
180182
181183 /* Get the kprobe at this addr (if any). Must have called lock_kprobes */
182184 struct kprobe *get_kprobe(void *addr);
kernel/kprobes.c+101−0
@@ -36,6 +36,7 @@ · 35 unmodified lines
3636 #include <linux/hash.h>
3737 #include <linux/init.h>
3838 #include <linux/module.h>
39+#include <linux/moduleloader.h>
3940 #include <asm/cacheflush.h>
4041 #include <asm/errno.h>
4142 #include <asm/kdebug.h>
@@ -50,6 +51,106 @@ unsigned int kprobe_cpu = NR_CPUS; · 8 unmodified lines
5051 static DEFINE_SPINLOCK(kprobe_lock);
5152 static struct kprobe *curr_kprobe;
5253
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+
62+struct 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+
69+static 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+ */
75+kprobe_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+
120+void 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+
53154 /* Locks kprobe: irqs must be disabled */
54155 void lock_kprobes(void)
55156 {

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t