diff options
author | Jason Baron <jbaron@redhat.com> | 2010-10-01 17:23:48 -0400 |
---|---|---|
committer | Steven Rostedt <rostedt@goodmis.org> | 2010-10-28 09:17:40 -0400 |
commit | 91bad2f8d3057482b9afb599f14421b007136960 (patch) | |
tree | ea5e09e74107593dcfc192c65c1395ed22674df4 | |
parent | b842f8faf6c7dc2005c6a70631c1a91bac02f180 (diff) |
jump label: Fix deadlock b/w jump_label_mutex vs. text_mutex
register_kprobe() downs the 'text_mutex' and then calls
jump_label_text_reserved(), which downs the 'jump_label_mutex'.
However, the jump label code takes those mutexes in the reverse
order.
Fix by requiring the caller of jump_label_text_reserved() to do
the jump label locking via the newly added: jump_label_lock(),
jump_label_unlock(). Currently, kprobes is the only user
of jump_label_text_reserved().
Reported-by: Ingo Molnar <mingo@elte.hu>
Acked-by: Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com>
Signed-off-by: Jason Baron <jbaron@redhat.com>
LKML-Reference: <759032c48d5e30c27f0bba003d09bffa8e9f28bb.1285965957.git.jbaron@redhat.com>
Signed-off-by: Steven Rostedt <rostedt@goodmis.org>
-rw-r--r-- | include/linux/jump_label.h | 5 | ||||
-rw-r--r-- | kernel/jump_label.c | 33 | ||||
-rw-r--r-- | kernel/kprobes.c | 6 |
3 files changed, 32 insertions, 12 deletions
diff --git a/include/linux/jump_label.h b/include/linux/jump_label.h index b67cb180e6e9..1947a1212678 100644 --- a/include/linux/jump_label.h +++ b/include/linux/jump_label.h | |||
@@ -18,6 +18,8 @@ struct module; | |||
18 | extern struct jump_entry __start___jump_table[]; | 18 | extern struct jump_entry __start___jump_table[]; |
19 | extern struct jump_entry __stop___jump_table[]; | 19 | extern struct jump_entry __stop___jump_table[]; |
20 | 20 | ||
21 | extern void jump_label_lock(void); | ||
22 | extern void jump_label_unlock(void); | ||
21 | extern void arch_jump_label_transform(struct jump_entry *entry, | 23 | extern void arch_jump_label_transform(struct jump_entry *entry, |
22 | enum jump_label_type type); | 24 | enum jump_label_type type); |
23 | extern void arch_jump_label_text_poke_early(jump_label_t addr); | 25 | extern void arch_jump_label_text_poke_early(jump_label_t addr); |
@@ -59,6 +61,9 @@ static inline int jump_label_text_reserved(void *start, void *end) | |||
59 | return 0; | 61 | return 0; |
60 | } | 62 | } |
61 | 63 | ||
64 | static inline void jump_label_lock(void) {} | ||
65 | static inline void jump_label_unlock(void) {} | ||
66 | |||
62 | #endif | 67 | #endif |
63 | 68 | ||
64 | #define COND_STMT(key, stmt) \ | 69 | #define COND_STMT(key, stmt) \ |
diff --git a/kernel/jump_label.c b/kernel/jump_label.c index be9e105345eb..12cce78e9568 100644 --- a/kernel/jump_label.c +++ b/kernel/jump_label.c | |||
@@ -39,6 +39,16 @@ struct jump_label_module_entry { | |||
39 | struct module *mod; | 39 | struct module *mod; |
40 | }; | 40 | }; |
41 | 41 | ||
42 | void jump_label_lock(void) | ||
43 | { | ||
44 | mutex_lock(&jump_label_mutex); | ||
45 | } | ||
46 | |||
47 | void jump_label_unlock(void) | ||
48 | { | ||
49 | mutex_unlock(&jump_label_mutex); | ||
50 | } | ||
51 | |||
42 | static int jump_label_cmp(const void *a, const void *b) | 52 | static int jump_label_cmp(const void *a, const void *b) |
43 | { | 53 | { |
44 | const struct jump_entry *jea = a; | 54 | const struct jump_entry *jea = a; |
@@ -152,7 +162,7 @@ void jump_label_update(unsigned long key, enum jump_label_type type) | |||
152 | struct jump_label_module_entry *e_module; | 162 | struct jump_label_module_entry *e_module; |
153 | int count; | 163 | int count; |
154 | 164 | ||
155 | mutex_lock(&jump_label_mutex); | 165 | jump_label_lock(); |
156 | entry = get_jump_label_entry((jump_label_t)key); | 166 | entry = get_jump_label_entry((jump_label_t)key); |
157 | if (entry) { | 167 | if (entry) { |
158 | count = entry->nr_entries; | 168 | count = entry->nr_entries; |
@@ -175,7 +185,7 @@ void jump_label_update(unsigned long key, enum jump_label_type type) | |||
175 | } | 185 | } |
176 | } | 186 | } |
177 | } | 187 | } |
178 | mutex_unlock(&jump_label_mutex); | 188 | jump_label_unlock(); |
179 | } | 189 | } |
180 | 190 | ||
181 | static int addr_conflict(struct jump_entry *entry, void *start, void *end) | 191 | static int addr_conflict(struct jump_entry *entry, void *start, void *end) |
@@ -232,6 +242,7 @@ out: | |||
232 | * overlaps with any of the jump label patch addresses. Code | 242 | * overlaps with any of the jump label patch addresses. Code |
233 | * that wants to modify kernel text should first verify that | 243 | * that wants to modify kernel text should first verify that |
234 | * it does not overlap with any of the jump label addresses. | 244 | * it does not overlap with any of the jump label addresses. |
245 | * Caller must hold jump_label_mutex. | ||
235 | * | 246 | * |
236 | * returns 1 if there is an overlap, 0 otherwise | 247 | * returns 1 if there is an overlap, 0 otherwise |
237 | */ | 248 | */ |
@@ -242,7 +253,6 @@ int jump_label_text_reserved(void *start, void *end) | |||
242 | struct jump_entry *iter_stop = __start___jump_table; | 253 | struct jump_entry *iter_stop = __start___jump_table; |
243 | int conflict = 0; | 254 | int conflict = 0; |
244 | 255 | ||
245 | mutex_lock(&jump_label_mutex); | ||
246 | iter = iter_start; | 256 | iter = iter_start; |
247 | while (iter < iter_stop) { | 257 | while (iter < iter_stop) { |
248 | if (addr_conflict(iter, start, end)) { | 258 | if (addr_conflict(iter, start, end)) { |
@@ -257,7 +267,6 @@ int jump_label_text_reserved(void *start, void *end) | |||
257 | conflict = module_conflict(start, end); | 267 | conflict = module_conflict(start, end); |
258 | #endif | 268 | #endif |
259 | out: | 269 | out: |
260 | mutex_unlock(&jump_label_mutex); | ||
261 | return conflict; | 270 | return conflict; |
262 | } | 271 | } |
263 | 272 | ||
@@ -268,7 +277,7 @@ static __init int init_jump_label(void) | |||
268 | struct jump_entry *iter_stop = __stop___jump_table; | 277 | struct jump_entry *iter_stop = __stop___jump_table; |
269 | struct jump_entry *iter; | 278 | struct jump_entry *iter; |
270 | 279 | ||
271 | mutex_lock(&jump_label_mutex); | 280 | jump_label_lock(); |
272 | ret = build_jump_label_hashtable(__start___jump_table, | 281 | ret = build_jump_label_hashtable(__start___jump_table, |
273 | __stop___jump_table); | 282 | __stop___jump_table); |
274 | iter = iter_start; | 283 | iter = iter_start; |
@@ -276,7 +285,7 @@ static __init int init_jump_label(void) | |||
276 | arch_jump_label_text_poke_early(iter->code); | 285 | arch_jump_label_text_poke_early(iter->code); |
277 | iter++; | 286 | iter++; |
278 | } | 287 | } |
279 | mutex_unlock(&jump_label_mutex); | 288 | jump_label_unlock(); |
280 | return ret; | 289 | return ret; |
281 | } | 290 | } |
282 | early_initcall(init_jump_label); | 291 | early_initcall(init_jump_label); |
@@ -409,21 +418,21 @@ jump_label_module_notify(struct notifier_block *self, unsigned long val, | |||
409 | 418 | ||
410 | switch (val) { | 419 | switch (val) { |
411 | case MODULE_STATE_COMING: | 420 | case MODULE_STATE_COMING: |
412 | mutex_lock(&jump_label_mutex); | 421 | jump_label_lock(); |
413 | ret = add_jump_label_module(mod); | 422 | ret = add_jump_label_module(mod); |
414 | if (ret) | 423 | if (ret) |
415 | remove_jump_label_module(mod); | 424 | remove_jump_label_module(mod); |
416 | mutex_unlock(&jump_label_mutex); | 425 | jump_label_unlock(); |
417 | break; | 426 | break; |
418 | case MODULE_STATE_GOING: | 427 | case MODULE_STATE_GOING: |
419 | mutex_lock(&jump_label_mutex); | 428 | jump_label_lock(); |
420 | remove_jump_label_module(mod); | 429 | remove_jump_label_module(mod); |
421 | mutex_unlock(&jump_label_mutex); | 430 | jump_label_unlock(); |
422 | break; | 431 | break; |
423 | case MODULE_STATE_LIVE: | 432 | case MODULE_STATE_LIVE: |
424 | mutex_lock(&jump_label_mutex); | 433 | jump_label_lock(); |
425 | remove_jump_label_module_init(mod); | 434 | remove_jump_label_module_init(mod); |
426 | mutex_unlock(&jump_label_mutex); | 435 | jump_label_unlock(); |
427 | break; | 436 | break; |
428 | } | 437 | } |
429 | return ret; | 438 | return ret; |
diff --git a/kernel/kprobes.c b/kernel/kprobes.c index 99865c33a60d..9437e14f36bd 100644 --- a/kernel/kprobes.c +++ b/kernel/kprobes.c | |||
@@ -1146,13 +1146,16 @@ int __kprobes register_kprobe(struct kprobe *p) | |||
1146 | return ret; | 1146 | return ret; |
1147 | 1147 | ||
1148 | preempt_disable(); | 1148 | preempt_disable(); |
1149 | jump_label_lock(); | ||
1149 | if (!kernel_text_address((unsigned long) p->addr) || | 1150 | if (!kernel_text_address((unsigned long) p->addr) || |
1150 | in_kprobes_functions((unsigned long) p->addr) || | 1151 | in_kprobes_functions((unsigned long) p->addr) || |
1151 | ftrace_text_reserved(p->addr, p->addr) || | 1152 | ftrace_text_reserved(p->addr, p->addr) || |
1152 | jump_label_text_reserved(p->addr, p->addr)) { | 1153 | jump_label_text_reserved(p->addr, p->addr)) { |
1153 | preempt_enable(); | 1154 | preempt_enable(); |
1155 | jump_label_unlock(); | ||
1154 | return -EINVAL; | 1156 | return -EINVAL; |
1155 | } | 1157 | } |
1158 | jump_label_unlock(); | ||
1156 | 1159 | ||
1157 | /* User can pass only KPROBE_FLAG_DISABLED to register_kprobe */ | 1160 | /* User can pass only KPROBE_FLAG_DISABLED to register_kprobe */ |
1158 | p->flags &= KPROBE_FLAG_DISABLED; | 1161 | p->flags &= KPROBE_FLAG_DISABLED; |
@@ -1187,6 +1190,8 @@ int __kprobes register_kprobe(struct kprobe *p) | |||
1187 | INIT_LIST_HEAD(&p->list); | 1190 | INIT_LIST_HEAD(&p->list); |
1188 | mutex_lock(&kprobe_mutex); | 1191 | mutex_lock(&kprobe_mutex); |
1189 | 1192 | ||
1193 | jump_label_lock(); /* needed to call jump_label_text_reserved() */ | ||
1194 | |||
1190 | get_online_cpus(); /* For avoiding text_mutex deadlock. */ | 1195 | get_online_cpus(); /* For avoiding text_mutex deadlock. */ |
1191 | mutex_lock(&text_mutex); | 1196 | mutex_lock(&text_mutex); |
1192 | 1197 | ||
@@ -1214,6 +1219,7 @@ int __kprobes register_kprobe(struct kprobe *p) | |||
1214 | out: | 1219 | out: |
1215 | mutex_unlock(&text_mutex); | 1220 | mutex_unlock(&text_mutex); |
1216 | put_online_cpus(); | 1221 | put_online_cpus(); |
1222 | jump_label_unlock(); | ||
1217 | mutex_unlock(&kprobe_mutex); | 1223 | mutex_unlock(&kprobe_mutex); |
1218 | 1224 | ||
1219 | if (probed_mod) | 1225 | if (probed_mod) |