diff options
-rw-r--r-- | arch/x86/kernel/kprobes.c | 3 | ||||
-rw-r--r-- | include/linux/jump_label.h | 8 | ||||
-rw-r--r-- | kernel/jump_label.c | 83 | ||||
-rw-r--r-- | kernel/kprobes.c | 3 |
4 files changed, 94 insertions, 3 deletions
diff --git a/arch/x86/kernel/kprobes.c b/arch/x86/kernel/kprobes.c index e05952af5d26..1cbd54c0df99 100644 --- a/arch/x86/kernel/kprobes.c +++ b/arch/x86/kernel/kprobes.c | |||
@@ -1218,7 +1218,8 @@ static int __kprobes copy_optimized_instructions(u8 *dest, u8 *src) | |||
1218 | } | 1218 | } |
1219 | /* Check whether the address range is reserved */ | 1219 | /* Check whether the address range is reserved */ |
1220 | if (ftrace_text_reserved(src, src + len - 1) || | 1220 | if (ftrace_text_reserved(src, src + len - 1) || |
1221 | alternatives_text_reserved(src, src + len - 1)) | 1221 | alternatives_text_reserved(src, src + len - 1) || |
1222 | jump_label_text_reserved(src, src + len - 1)) | ||
1222 | return -EBUSY; | 1223 | return -EBUSY; |
1223 | 1224 | ||
1224 | return len; | 1225 | return len; |
diff --git a/include/linux/jump_label.h b/include/linux/jump_label.h index de58656d28e0..b72cd9f92c2e 100644 --- a/include/linux/jump_label.h +++ b/include/linux/jump_label.h | |||
@@ -20,9 +20,10 @@ extern struct jump_entry __stop___jump_table[]; | |||
20 | 20 | ||
21 | extern void arch_jump_label_transform(struct jump_entry *entry, | 21 | extern void arch_jump_label_transform(struct jump_entry *entry, |
22 | enum jump_label_type type); | 22 | enum jump_label_type type); |
23 | extern void arch_jump_label_text_poke_early(jump_label_t addr); | ||
23 | extern void jump_label_update(unsigned long key, enum jump_label_type type); | 24 | extern void jump_label_update(unsigned long key, enum jump_label_type type); |
24 | extern void jump_label_apply_nops(struct module *mod); | 25 | extern void jump_label_apply_nops(struct module *mod); |
25 | extern void arch_jump_label_text_poke_early(jump_label_t addr); | 26 | extern int jump_label_text_reserved(void *start, void *end); |
26 | 27 | ||
27 | #define enable_jump_label(key) \ | 28 | #define enable_jump_label(key) \ |
28 | jump_label_update((unsigned long)key, JUMP_LABEL_ENABLE); | 29 | jump_label_update((unsigned long)key, JUMP_LABEL_ENABLE); |
@@ -53,6 +54,11 @@ static inline int jump_label_apply_nops(struct module *mod) | |||
53 | return 0; | 54 | return 0; |
54 | } | 55 | } |
55 | 56 | ||
57 | static inline int jump_label_text_reserved(void *start, void *end) | ||
58 | { | ||
59 | return 0; | ||
60 | } | ||
61 | |||
56 | #endif | 62 | #endif |
57 | 63 | ||
58 | #endif | 64 | #endif |
diff --git a/kernel/jump_label.c b/kernel/jump_label.c index 460fd40112b3..7be868bf25c6 100644 --- a/kernel/jump_label.c +++ b/kernel/jump_label.c | |||
@@ -177,6 +177,89 @@ void jump_label_update(unsigned long key, enum jump_label_type type) | |||
177 | mutex_unlock(&jump_label_mutex); | 177 | mutex_unlock(&jump_label_mutex); |
178 | } | 178 | } |
179 | 179 | ||
180 | static int addr_conflict(struct jump_entry *entry, void *start, void *end) | ||
181 | { | ||
182 | if (entry->code <= (unsigned long)end && | ||
183 | entry->code + JUMP_LABEL_NOP_SIZE > (unsigned long)start) | ||
184 | return 1; | ||
185 | |||
186 | return 0; | ||
187 | } | ||
188 | |||
189 | #ifdef CONFIG_MODULES | ||
190 | |||
191 | static int module_conflict(void *start, void *end) | ||
192 | { | ||
193 | struct hlist_head *head; | ||
194 | struct hlist_node *node, *node_next, *module_node, *module_node_next; | ||
195 | struct jump_label_entry *e; | ||
196 | struct jump_label_module_entry *e_module; | ||
197 | struct jump_entry *iter; | ||
198 | int i, count; | ||
199 | int conflict = 0; | ||
200 | |||
201 | for (i = 0; i < JUMP_LABEL_TABLE_SIZE; i++) { | ||
202 | head = &jump_label_table[i]; | ||
203 | hlist_for_each_entry_safe(e, node, node_next, head, hlist) { | ||
204 | hlist_for_each_entry_safe(e_module, module_node, | ||
205 | module_node_next, | ||
206 | &(e->modules), hlist) { | ||
207 | count = e_module->nr_entries; | ||
208 | iter = e_module->table; | ||
209 | while (count--) { | ||
210 | if (addr_conflict(iter, start, end)) { | ||
211 | conflict = 1; | ||
212 | goto out; | ||
213 | } | ||
214 | iter++; | ||
215 | } | ||
216 | } | ||
217 | } | ||
218 | } | ||
219 | out: | ||
220 | return conflict; | ||
221 | } | ||
222 | |||
223 | #endif | ||
224 | |||
225 | /*** | ||
226 | * jump_label_text_reserved - check if addr range is reserved | ||
227 | * @start: start text addr | ||
228 | * @end: end text addr | ||
229 | * | ||
230 | * checks if the text addr located between @start and @end | ||
231 | * overlaps with any of the jump label patch addresses. Code | ||
232 | * that wants to modify kernel text should first verify that | ||
233 | * it does not overlap with any of the jump label addresses. | ||
234 | * | ||
235 | * returns 1 if there is an overlap, 0 otherwise | ||
236 | */ | ||
237 | int jump_label_text_reserved(void *start, void *end) | ||
238 | { | ||
239 | struct jump_entry *iter; | ||
240 | struct jump_entry *iter_start = __start___jump_table; | ||
241 | struct jump_entry *iter_stop = __start___jump_table; | ||
242 | int conflict = 0; | ||
243 | |||
244 | mutex_lock(&jump_label_mutex); | ||
245 | iter = iter_start; | ||
246 | while (iter < iter_stop) { | ||
247 | if (addr_conflict(iter, start, end)) { | ||
248 | conflict = 1; | ||
249 | goto out; | ||
250 | } | ||
251 | iter++; | ||
252 | } | ||
253 | |||
254 | /* now check modules */ | ||
255 | #ifdef CONFIG_MODULES | ||
256 | conflict = module_conflict(start, end); | ||
257 | #endif | ||
258 | out: | ||
259 | mutex_unlock(&jump_label_mutex); | ||
260 | return conflict; | ||
261 | } | ||
262 | |||
180 | static __init int init_jump_label(void) | 263 | static __init int init_jump_label(void) |
181 | { | 264 | { |
182 | int ret; | 265 | int ret; |
diff --git a/kernel/kprobes.c b/kernel/kprobes.c index 18904e42a918..ec4210c6501e 100644 --- a/kernel/kprobes.c +++ b/kernel/kprobes.c | |||
@@ -1147,7 +1147,8 @@ int __kprobes register_kprobe(struct kprobe *p) | |||
1147 | preempt_disable(); | 1147 | preempt_disable(); |
1148 | if (!kernel_text_address((unsigned long) p->addr) || | 1148 | if (!kernel_text_address((unsigned long) p->addr) || |
1149 | in_kprobes_functions((unsigned long) p->addr) || | 1149 | in_kprobes_functions((unsigned long) p->addr) || |
1150 | ftrace_text_reserved(p->addr, p->addr)) { | 1150 | ftrace_text_reserved(p->addr, p->addr) || |
1151 | jump_label_text_reserved(p->addr, p->addr)) { | ||
1151 | preempt_enable(); | 1152 | preempt_enable(); |
1152 | return -EINVAL; | 1153 | return -EINVAL; |
1153 | } | 1154 | } |