Skip to content

Commit a949ae5

Browse files
committed
ftrace/module: Hardcode ftrace_module_init() call into load_module()
A race exists between module loading and enabling of function tracer. CPU 1 CPU 2 ----- ----- load_module() module->state = MODULE_STATE_COMING register_ftrace_function() mutex_lock(&ftrace_lock); ftrace_startup() update_ftrace_function(); ftrace_arch_code_modify_prepare() set_all_module_text_rw(); <enables-ftrace> ftrace_arch_code_modify_post_process() set_all_module_text_ro(); [ here all module text is set to RO, including the module that is loading!! ] blocking_notifier_call_chain(MODULE_STATE_COMING); ftrace_init_module() [ tries to modify code, but it's RO, and fails! ftrace_bug() is called] When this race happens, ftrace_bug() will produces a nasty warning and all of the function tracing features will be disabled until reboot. The simple solution is to treate module load the same way the core kernel is treated at boot. To hardcode the ftrace function modification of converting calls to mcount into nops. This is done in init/main.c there's no reason it could not be done in load_module(). This gives a better control of the changes and doesn't tie the state of the module to its notifiers as much. Ftrace is special, it needs to be treated as such. The reason this would work, is that the ftrace_module_init() would be called while the module is in MODULE_STATE_UNFORMED, which is ignored by the set_all_module_text_ro() call. Link: http://lkml.kernel.org/r/[email protected] Reported-by: Takao Indoh <[email protected]> Acked-by: Rusty Russell <[email protected]> Cc: [email protected] # 2.6.38+ Signed-off-by: Steven Rostedt <[email protected]>
1 parent a798c10 commit a949ae5

File tree

3 files changed

+9
-23
lines changed

3 files changed

+9
-23
lines changed

include/linux/ftrace.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -535,6 +535,7 @@ static inline int ftrace_modify_call(struct dyn_ftrace *rec, unsigned long old_a
535535
extern int ftrace_arch_read_dyn_info(char *buf, int size);
536536

537537
extern int skip_trace(unsigned long ip);
538+
extern void ftrace_module_init(struct module *mod);
538539

539540
extern void ftrace_disable_daemon(void);
540541
extern void ftrace_enable_daemon(void);
@@ -544,6 +545,7 @@ static inline int ftrace_force_update(void) { return 0; }
544545
static inline void ftrace_disable_daemon(void) { }
545546
static inline void ftrace_enable_daemon(void) { }
546547
static inline void ftrace_release_mod(struct module *mod) {}
548+
static inline void ftrace_module_init(struct module *mod) {}
547549
static inline __init int register_ftrace_command(struct ftrace_func_command *cmd)
548550
{
549551
return -EINVAL;

kernel/module.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3271,6 +3271,9 @@ static int load_module(struct load_info *info, const char __user *uargs,
32713271

32723272
dynamic_debug_setup(info->debug, info->num_debug);
32733273

3274+
/* Ftrace init must be called in the MODULE_STATE_UNFORMED state */
3275+
ftrace_module_init(mod);
3276+
32743277
/* Finally it's fully formed, ready to start executing. */
32753278
err = complete_formation(mod, info);
32763279
if (err)

kernel/trace/ftrace.c

Lines changed: 4 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -4330,16 +4330,11 @@ static void ftrace_init_module(struct module *mod,
43304330
ftrace_process_locs(mod, start, end);
43314331
}
43324332

4333-
static int ftrace_module_notify_enter(struct notifier_block *self,
4334-
unsigned long val, void *data)
4333+
void ftrace_module_init(struct module *mod)
43354334
{
4336-
struct module *mod = data;
4337-
4338-
if (val == MODULE_STATE_COMING)
4339-
ftrace_init_module(mod, mod->ftrace_callsites,
4340-
mod->ftrace_callsites +
4341-
mod->num_ftrace_callsites);
4342-
return 0;
4335+
ftrace_init_module(mod, mod->ftrace_callsites,
4336+
mod->ftrace_callsites +
4337+
mod->num_ftrace_callsites);
43434338
}
43444339

43454340
static int ftrace_module_notify_exit(struct notifier_block *self,
@@ -4353,23 +4348,13 @@ static int ftrace_module_notify_exit(struct notifier_block *self,
43534348
return 0;
43544349
}
43554350
#else
4356-
static int ftrace_module_notify_enter(struct notifier_block *self,
4357-
unsigned long val, void *data)
4358-
{
4359-
return 0;
4360-
}
43614351
static int ftrace_module_notify_exit(struct notifier_block *self,
43624352
unsigned long val, void *data)
43634353
{
43644354
return 0;
43654355
}
43664356
#endif /* CONFIG_MODULES */
43674357

4368-
struct notifier_block ftrace_module_enter_nb = {
4369-
.notifier_call = ftrace_module_notify_enter,
4370-
.priority = INT_MAX, /* Run before anything that can use kprobes */
4371-
};
4372-
43734358
struct notifier_block ftrace_module_exit_nb = {
43744359
.notifier_call = ftrace_module_notify_exit,
43754360
.priority = INT_MIN, /* Run after anything that can remove kprobes */
@@ -4403,10 +4388,6 @@ void __init ftrace_init(void)
44034388
__start_mcount_loc,
44044389
__stop_mcount_loc);
44054390

4406-
ret = register_module_notifier(&ftrace_module_enter_nb);
4407-
if (ret)
4408-
pr_warning("Failed to register trace ftrace module enter notifier\n");
4409-
44104391
ret = register_module_notifier(&ftrace_module_exit_nb);
44114392
if (ret)
44124393
pr_warning("Failed to register trace ftrace module exit notifier\n");

0 commit comments

Comments
 (0)