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 | |
| 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')
| -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 | } |
