Skip to contentSkip to conversation

ftrace: Add more checks to ignore disabled functions

GIT PULL New Steven Rostedt 1 participant 3 messages Nov 15, 2016

Subsystems (3)
lore
Share & tools
lore

Steven Rostedt

author maintainer
#0 lore raw

Linus,

Alexei discovered a race condition in modules failing to load that can cause a ftrace check to trigger and disable ftrace. This is because of the way modules are registered to ftrace. Their functions are loaded in the ftrace function tables but set to "disabled" since they are still in the process of being loaded by the module. After the module is finished, it calls back into the ftrace infrastructure to enable it. Looking deeper into the locations that access all the functions in the table, I found more locations that should ignore the disabled ones.

Please pull the latest trace-v4.9-rc5 tree, which can be found at:

git://git.kernel.org/pub/scm/linux/kernel/git/rostedt/linux-trace.git
trace-v4.9-rc5

Tag SHA1: 0f6a062b1a2a55e2c0f801514e9f8792e4339aef Head SHA1: 546fece4eae871f033925ccf0ff2b740725ae915

Alexei Starovoitov (1):
ftrace: Ignore FTRACE_FL_DISABLED while walking dyn_ftrace records

Steven Rostedt (Red Hat) (1):
ftrace: Add more checks for FTRACE_FL_DISABLED in processing ip records

----

 kernel/trace/ftrace.c | 24 +++++++++++++++++++++++-
 1 file changed, 23 insertions(+), 1 deletion(-)
author maintainer
#1 lore raw

PATCH 1/2 ftrace: Ignore FTRACE_FL_DISABLED while walking dyn_ftrace records

From: Alexei Starovoitov <[email protected]>

ftrace_shutdown() checks for sanity of ftrace records
and if dyn_ftrace->flags is not zero, it will warn.
It can happen that 'flags' are set to FTRACE_FL_DISABLED at this point, since some module was loaded, but before ftrace_module_enable() cleared the flags for this module.

In other words the module.c is doing:
ftrace_module_init(mod); // calls ftrace_update_code() that sets flags=FTRACE_FL_DISABLED ... // here ftrace_shutdown() is called that warns, since
err = prepare_coming_module(mod); // didn't have a chance to clear FTRACE_FL_DISABLED

Fix it by ignoring disabled records.
It's similar to what __ftrace_hash_rec_update() is already doing.

Fixes b7ffffbb46f2 "ftrace: Add infrastructure for delayed enabling of module functions"
---
 kernel/trace/ftrace.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
kernel/trace/ftrace.c +1 -1
index 2050a7652a86..326498baab83 100644
@@ -2763,7 +2763,7 @@ static int ftrace_shutdown(struct ftrace_ops *ops, int command)
 		struct dyn_ftrace *rec;
 
 		do_for_each_ftrace_rec(pg, rec) {
-			if (FTRACE_WARN_ON_ONCE(rec->flags))
+			if (FTRACE_WARN_ON_ONCE(rec->flags & ~FTRACE_FL_DISABLED))
 				pr_warn("  %pS flags:%lx\n",
 					(void *)rec->ip, rec->flags);
 		} while_for_each_ftrace_rec();
Signature · 1 line
2.10.2
author maintainer
#2 lore raw

PATCH 2/2 ftrace: Add more checks for FTRACE_FL_DISABLED in processing ip records

In reply to Steven Rostedt

From: "Steven Rostedt (Red Hat)" <[email protected]>

When a module is first loaded and its function ip records are added to the ftrace list of functions to modify, they are set to DISABLED, as their text is still in a read only state. When the module is fully loaded, and can be updated, the flag is cleared, and if their's any functions that should be tracing them, it is updated at that moment.

But there's several locations that do record accounting and should ignore records that are marked as disabled, or they can cause issues.

Alexei already fixed one location, but others need to be addressed.

Fixes b7ffffbb46f2 "ftrace: Add infrastructure for delayed enabling of module functions"
---
 kernel/trace/ftrace.c | 22 ++++++++++++++++++++++
 1 file changed, 22 insertions(+)
kernel/trace/ftrace.c +22 -0
index 326498baab83..da87b3cba5b3 100644
@@ -1862,6 +1862,10 @@ static int __ftrace_hash_update_ipmodify(struct ftrace_ops *ops,
 
 	/* Update rec->flags */
 	do_for_each_ftrace_rec(pg, rec) {
+
+		if (rec->flags & FTRACE_FL_DISABLED)
+			continue;
+
 		/* We need to update only differences of filter_hash */
 		in_old = !!ftrace_lookup_ip(old_hash, rec->ip);
 		in_new = !!ftrace_lookup_ip(new_hash, rec->ip);
@@ -1884,6 +1888,10 @@ rollback:
 
 	/* Roll back what we did above */
 	do_for_each_ftrace_rec(pg, rec) {
+
+		if (rec->flags & FTRACE_FL_DISABLED)
+			continue;
+
 		if (rec == end)
 			goto err_out;
 
@@ -2397,6 +2405,10 @@ void __weak ftrace_replace_code(int enable)
 		return;
 
 	do_for_each_ftrace_rec(pg, rec) {
+
+		if (rec->flags & FTRACE_FL_DISABLED)
+			continue;
+
 		failed = __ftrace_replace_code(rec, enable);
 		if (failed) {
 			ftrace_bug(failed, rec);
@@ -3598,6 +3610,10 @@ match_records(struct ftrace_hash *hash, char *func, int len, char *mod)
 		goto out_unlock;
 
 	do_for_each_ftrace_rec(pg, rec) {
+
+		if (rec->flags & FTRACE_FL_DISABLED)
+			continue;
+
 		if (ftrace_match_record(rec, &func_g, mod_match, exclude_mod)) {
 			ret = enter_record(hash, rec, clear_filter);
 			if (ret < 0) {
@@ -3793,6 +3809,9 @@ register_ftrace_function_probe(char *glob, struct ftrace_probe_ops *ops,
 
 	do_for_each_ftrace_rec(pg, rec) {
 
+		if (rec->flags & FTRACE_FL_DISABLED)
+			continue;
+
 		if (!ftrace_match_record(rec, &func_g, NULL, 0))
 			continue;
 
@@ -4685,6 +4704,9 @@ ftrace_set_func(unsigned long *array, int *idx, int size, char *buffer)
 
 	do_for_each_ftrace_rec(pg, rec) {
 
+		if (rec->flags & FTRACE_FL_DISABLED)
+			continue;
+
 		if (ftrace_match_record(rec, &func_g, NULL, 0)) {
 			/* if it is in the array */
 			exists = false;
Signature · 1 line
2.10.2