Commit d9550cf732c439447abe24655c23fcc27915ff7d

Authored by Steven Rostedt (Red Hat)
Committed by Greg Kroah-Hartman
1 parent bb4d8694e0

ftrace/module: Hardcode ftrace_module_init() call into load_module()

commit a949ae560a511fe4e3adf48fa44fefded93e5c2b upstream.

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/1395637826-3312-1-git-send-email-indou.takao@jp.fujitsu.com

Reported-by: Takao Indoh <indou.takao@jp.fujitsu.com>
Acked-by: Rusty Russell <rusty@rustcorp.com.au>
Signed-off-by: Steven Rostedt <rostedt@goodmis.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>

Showing 3 changed files with 9 additions and 23 deletions Side-by-side Diff

include/linux/ftrace.h
... ... @@ -524,6 +524,7 @@
524 524 extern int ftrace_arch_read_dyn_info(char *buf, int size);
525 525  
526 526 extern int skip_trace(unsigned long ip);
  527 +extern void ftrace_module_init(struct module *mod);
527 528  
528 529 extern void ftrace_disable_daemon(void);
529 530 extern void ftrace_enable_daemon(void);
... ... @@ -533,6 +534,7 @@
533 534 static inline void ftrace_disable_daemon(void) { }
534 535 static inline void ftrace_enable_daemon(void) { }
535 536 static inline void ftrace_release_mod(struct module *mod) {}
  537 +static inline void ftrace_module_init(struct module *mod) {}
536 538 static inline __init int register_ftrace_command(struct ftrace_func_command *cmd)
537 539 {
538 540 return -EINVAL;
... ... @@ -3265,6 +3265,9 @@
3265 3265  
3266 3266 dynamic_debug_setup(info->debug, info->num_debug);
3267 3267  
  3268 + /* Ftrace init must be called in the MODULE_STATE_UNFORMED state */
  3269 + ftrace_module_init(mod);
  3270 +
3268 3271 /* Finally it's fully formed, ready to start executing. */
3269 3272 err = complete_formation(mod, info);
3270 3273 if (err)
kernel/trace/ftrace.c
... ... @@ -4315,16 +4315,11 @@
4315 4315 ftrace_process_locs(mod, start, end);
4316 4316 }
4317 4317  
4318   -static int ftrace_module_notify_enter(struct notifier_block *self,
4319   - unsigned long val, void *data)
  4318 +void ftrace_module_init(struct module *mod)
4320 4319 {
4321   - struct module *mod = data;
4322   -
4323   - if (val == MODULE_STATE_COMING)
4324   - ftrace_init_module(mod, mod->ftrace_callsites,
4325   - mod->ftrace_callsites +
4326   - mod->num_ftrace_callsites);
4327   - return 0;
  4320 + ftrace_init_module(mod, mod->ftrace_callsites,
  4321 + mod->ftrace_callsites +
  4322 + mod->num_ftrace_callsites);
4328 4323 }
4329 4324  
4330 4325 static int ftrace_module_notify_exit(struct notifier_block *self,
... ... @@ -4338,11 +4333,6 @@
4338 4333 return 0;
4339 4334 }
4340 4335 #else
4341   -static int ftrace_module_notify_enter(struct notifier_block *self,
4342   - unsigned long val, void *data)
4343   -{
4344   - return 0;
4345   -}
4346 4336 static int ftrace_module_notify_exit(struct notifier_block *self,
4347 4337 unsigned long val, void *data)
4348 4338 {
... ... @@ -4350,11 +4340,6 @@
4350 4340 }
4351 4341 #endif /* CONFIG_MODULES */
4352 4342  
4353   -struct notifier_block ftrace_module_enter_nb = {
4354   - .notifier_call = ftrace_module_notify_enter,
4355   - .priority = INT_MAX, /* Run before anything that can use kprobes */
4356   -};
4357   -
4358 4343 struct notifier_block ftrace_module_exit_nb = {
4359 4344 .notifier_call = ftrace_module_notify_exit,
4360 4345 .priority = INT_MIN, /* Run after anything that can remove kprobes */
... ... @@ -4390,10 +4375,6 @@
4390 4375 ret = ftrace_process_locs(NULL,
4391 4376 __start_mcount_loc,
4392 4377 __stop_mcount_loc);
4393   -
4394   - ret = register_module_notifier(&ftrace_module_enter_nb);
4395   - if (ret)
4396   - pr_warning("Failed to register trace ftrace module enter notifier\n");
4397 4378  
4398 4379 ret = register_module_notifier(&ftrace_module_exit_nb);
4399 4380 if (ret)