Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit b842f8fa authored by Jason Baron's avatar Jason Baron Committed by Steven Rostedt
Browse files

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: default avatarMathieu Desnoyers <mathieu.desnoyers@efficios.com>
Cc: Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com>
Signed-off-by: default avatarJason 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: default avatarSteven Rostedt <rostedt@goodmis.org>
parent e3e1288e
Loading
Loading
Loading
Loading
+40 −1
Original line number Diff line number Diff line
@@ -168,7 +168,8 @@ void jump_label_update(unsigned long key, enum jump_label_type type)
			count = e_module->nr_entries;
			iter = e_module->table;
			while (count--) {
				if (kernel_text_address(iter->code))
				if (iter->key &&
						kernel_text_address(iter->code))
					arch_jump_label_transform(iter, type);
				iter++;
			}
@@ -366,6 +367,39 @@ static void remove_jump_label_module(struct module *mod)
	}
}

static void remove_jump_label_module_init(struct module *mod)
{
	struct hlist_head *head;
	struct hlist_node *node, *node_next, *module_node, *module_node_next;
	struct jump_label_entry *e;
	struct jump_label_module_entry *e_module;
	struct jump_entry *iter;
	int i, count;

	/* if the module doesn't have jump label entries, just return */
	if (!mod->num_jump_entries)
		return;

	for (i = 0; i < JUMP_LABEL_TABLE_SIZE; i++) {
		head = &jump_label_table[i];
		hlist_for_each_entry_safe(e, node, node_next, head, hlist) {
			hlist_for_each_entry_safe(e_module, module_node,
						  module_node_next,
						  &(e->modules), hlist) {
				if (e_module->mod != mod)
					continue;
				count = e_module->nr_entries;
				iter = e_module->table;
				while (count--) {
					if (within_module_init(iter->code, mod))
						iter->key = 0;
					iter++;
				}
			}
		}
	}
}

static int
jump_label_module_notify(struct notifier_block *self, unsigned long val,
			 void *data)
@@ -386,6 +420,11 @@ jump_label_module_notify(struct notifier_block *self, unsigned long val,
		remove_jump_label_module(mod);
		mutex_unlock(&jump_label_mutex);
		break;
	case MODULE_STATE_LIVE:
		mutex_lock(&jump_label_mutex);
		remove_jump_label_module_init(mod);
		mutex_unlock(&jump_label_mutex);
		break;
	}
	return ret;
}