diff options
author | Jason Baron <jbaron@redhat.com> | 2010-10-01 17:23:41 -0400 |
---|---|---|
committer | Steven Rostedt <rostedt@goodmis.org> | 2010-10-28 09:17:02 -0400 |
commit | b842f8faf6c7dc2005c6a70631c1a91bac02f180 (patch) | |
tree | fa4c5ed7136be2e39b728173fcae2385b119bcef /kernel/jump_label.c | |
parent | e3e1288e86a07cdeb0aee5860a2dff111c6eff79 (diff) |
jump label: Fix module __init section race
Jump label uses is_module_text_address() to ensure that the module
__init sections are valid before updating them. However, between the
check for a valid module __init section and the subsequent jump
label update, the module's __init section could be freed out from under
us.
We fix this potential race by adding a notifier callback to the
MODULE_STATE_LIVE state. This notifier is called *after* the __init
section has been run but before it is going to be freed. In the
callback, the jump label code zeros the key value for any __init jump
code within the module, and we add a check for a non-zero key value when
we update jump labels. In this way we require no additional data
structures.
Thanks to Mathieu Desnoyers for pointing out this race condition.
Reported-by: Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
Cc: Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com>
Signed-off-by: Jason Baron <jbaron@redhat.com>
LKML-Reference: <c6f037b7598777668025ceedd9294212fd95fa34.1285965957.git.jbaron@redhat.com>
[ Renamed remove_module_init() to remove_jump_label_module_init()
as suggested by Masami Hiramatsu. ]
Signed-off-by: Steven Rostedt <rostedt@goodmis.org>
Diffstat (limited to 'kernel/jump_label.c')
-rw-r--r-- | kernel/jump_label.c | 41 |
1 files changed, 40 insertions, 1 deletions
diff --git a/kernel/jump_label.c b/kernel/jump_label.c index 7be868bf25c6..be9e105345eb 100644 --- a/kernel/jump_label.c +++ b/kernel/jump_label.c | |||
@@ -168,7 +168,8 @@ void jump_label_update(unsigned long key, enum jump_label_type type) | |||
168 | count = e_module->nr_entries; | 168 | count = e_module->nr_entries; |
169 | iter = e_module->table; | 169 | iter = e_module->table; |
170 | while (count--) { | 170 | while (count--) { |
171 | if (kernel_text_address(iter->code)) | 171 | if (iter->key && |
172 | kernel_text_address(iter->code)) | ||
172 | arch_jump_label_transform(iter, type); | 173 | arch_jump_label_transform(iter, type); |
173 | iter++; | 174 | iter++; |
174 | } | 175 | } |
@@ -366,6 +367,39 @@ static void remove_jump_label_module(struct module *mod) | |||
366 | } | 367 | } |
367 | } | 368 | } |
368 | 369 | ||
370 | static void remove_jump_label_module_init(struct module *mod) | ||
371 | { | ||
372 | struct hlist_head *head; | ||
373 | struct hlist_node *node, *node_next, *module_node, *module_node_next; | ||
374 | struct jump_label_entry *e; | ||
375 | struct jump_label_module_entry *e_module; | ||
376 | struct jump_entry *iter; | ||
377 | int i, count; | ||
378 | |||
379 | /* if the module doesn't have jump label entries, just return */ | ||
380 | if (!mod->num_jump_entries) | ||
381 | return; | ||
382 | |||
383 | for (i = 0; i < JUMP_LABEL_TABLE_SIZE; i++) { | ||
384 | head = &jump_label_table[i]; | ||
385 | hlist_for_each_entry_safe(e, node, node_next, head, hlist) { | ||
386 | hlist_for_each_entry_safe(e_module, module_node, | ||
387 | module_node_next, | ||
388 | &(e->modules), hlist) { | ||
389 | if (e_module->mod != mod) | ||
390 | continue; | ||
391 | count = e_module->nr_entries; | ||
392 | iter = e_module->table; | ||
393 | while (count--) { | ||
394 | if (within_module_init(iter->code, mod)) | ||
395 | iter->key = 0; | ||
396 | iter++; | ||
397 | } | ||
398 | } | ||
399 | } | ||
400 | } | ||
401 | } | ||
402 | |||
369 | static int | 403 | static int |
370 | jump_label_module_notify(struct notifier_block *self, unsigned long val, | 404 | jump_label_module_notify(struct notifier_block *self, unsigned long val, |
371 | void *data) | 405 | void *data) |
@@ -386,6 +420,11 @@ jump_label_module_notify(struct notifier_block *self, unsigned long val, | |||
386 | remove_jump_label_module(mod); | 420 | remove_jump_label_module(mod); |
387 | mutex_unlock(&jump_label_mutex); | 421 | mutex_unlock(&jump_label_mutex); |
388 | break; | 422 | break; |
423 | case MODULE_STATE_LIVE: | ||
424 | mutex_lock(&jump_label_mutex); | ||
425 | remove_jump_label_module_init(mod); | ||
426 | mutex_unlock(&jump_label_mutex); | ||
427 | break; | ||
389 | } | 428 | } |
390 | return ret; | 429 | return ret; |
391 | } | 430 | } |