linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH 0/2] [GIT PULL][v3.0] tracing: Add refcount for system filter and enable
@ 2011-07-06 21:19 Steven Rostedt
  2011-07-06 21:19 ` [PATCH 1/2] tracing: Add ref_count to event systems for freeing Steven Rostedt
                   ` (2 more replies)
  0 siblings, 3 replies; 5+ messages in thread
From: Steven Rostedt @ 2011-07-06 21:19 UTC (permalink / raw)
  To: linux-kernel; +Cc: Ingo Molnar, Andrew Morton, Frederic Weisbecker


Ingo,

Please pull the latest tip/perf/urgent tree, which can be found at:

  git://git.kernel.org/pub/scm/linux/kernel/git/rostedt/linux-2.6-trace.git
tip/perf/urgent

Head SHA1: 57f124ca21d216fca3ba3995235c15855f9b9980


Steven Rostedt (2):
      tracing: Add ref_count to event systems for freeing
      tracing: Have enable system events use the subsystem_open routine

----
 kernel/trace/trace.h               |    1 +
 kernel/trace/trace_events.c        |  113 ++++++++++++++++++++++++++++++------
 kernel/trace/trace_events_filter.c |    6 ++
 3 files changed, 102 insertions(+), 18 deletions(-)

^ permalink raw reply	[flat|nested] 5+ messages in thread

* [PATCH 1/2] tracing: Add ref_count to event systems for freeing
  2011-07-06 21:19 [PATCH 0/2] [GIT PULL][v3.0] tracing: Add refcount for system filter and enable Steven Rostedt
@ 2011-07-06 21:19 ` Steven Rostedt
  2011-07-06 21:19 ` [PATCH 2/2] tracing: Have enable system events use the subsystem_open routine Steven Rostedt
  2011-07-07 14:15 ` [PATCH 0/2] [GIT PULL][v3.0] tracing: Add refcount for system filter and enable Ingo Molnar
  2 siblings, 0 replies; 5+ messages in thread
From: Steven Rostedt @ 2011-07-06 21:19 UTC (permalink / raw)
  To: linux-kernel
  Cc: Ingo Molnar, Andrew Morton, Frederic Weisbecker, stable, Johannes Berg

[-- Attachment #1: 0001-tracing-Add-ref_count-to-event-systems-for-freeing.patch --]
[-- Type: text/plain, Size: 5846 bytes --]

From: Steven Rostedt <srostedt@redhat.com>

The system is freed when its nr_events is set to zero. This happens
when a module created an event system and then later the module is
removed. Modules may share systems, so the system is allocated when
it is created and freed when the modules are unloaded and all the
events under the system are removed (nr_events set to zero).

The problem arises when a task opened the "filter" file for the
system. If the module is unloaded and it removed the last event for
that system, the system structure is freed. If the task that opened
the filter file accesses the "filter" file after the system has
been freed, the system will access an invalid pointer.

By adding a ref_count, and using it to keep track of what
is using the event system, we can free it after all users
are finished with the event system.

Cc: <stable@kernel.org>
Reported-by: Johannes Berg <johannes.berg@intel.com>
Signed-off-by: Steven Rostedt <rostedt@goodmis.org>
---
 kernel/trace/trace.h               |    1 +
 kernel/trace/trace_events.c        |   86 +++++++++++++++++++++++++++++++-----
 kernel/trace/trace_events_filter.c |    6 +++
 3 files changed, 82 insertions(+), 11 deletions(-)

diff --git a/kernel/trace/trace.h b/kernel/trace/trace.h
index 229f859..f807407 100644
--- a/kernel/trace/trace.h
+++ b/kernel/trace/trace.h
@@ -677,6 +677,7 @@ struct event_subsystem {
 	struct dentry		*entry;
 	struct event_filter	*filter;
 	int			nr_events;
+	int			ref_count;
 };
 
 #define FILTER_PRED_INVALID	((unsigned short)-1)
diff --git a/kernel/trace/trace_events.c b/kernel/trace/trace_events.c
index 686ec39..ffc5b28 100644
--- a/kernel/trace/trace_events.c
+++ b/kernel/trace/trace_events.c
@@ -244,6 +244,35 @@ static void ftrace_clear_events(void)
 	mutex_unlock(&event_mutex);
 }
 
+static void __put_system(struct event_subsystem *system)
+{
+	struct event_filter *filter = system->filter;
+
+	WARN_ON_ONCE(system->ref_count == 0);
+	if (--system->ref_count)
+		return;
+
+	if (filter) {
+		kfree(filter->filter_string);
+		kfree(filter);
+	}
+	kfree(system->name);
+	kfree(system);
+}
+
+static void __get_system(struct event_subsystem *system)
+{
+	WARN_ON_ONCE(system->ref_count == 0);
+	system->ref_count++;
+}
+
+static void put_system(struct event_subsystem *system)
+{
+	mutex_lock(&event_mutex);
+	__put_system(system);
+	mutex_unlock(&event_mutex);
+}
+
 /*
  * __ftrace_set_clr_event(NULL, NULL, NULL, set) will set/unset all events.
  */
@@ -826,6 +855,47 @@ event_filter_write(struct file *filp, const char __user *ubuf, size_t cnt,
 	return cnt;
 }
 
+static LIST_HEAD(event_subsystems);
+
+static int subsystem_open(struct inode *inode, struct file *filp)
+{
+	struct event_subsystem *system = NULL;
+	int ret;
+
+	/* Make sure the system still exists */
+	mutex_lock(&event_mutex);
+	list_for_each_entry(system, &event_subsystems, list) {
+		if (system == inode->i_private) {
+			/* Don't open systems with no events */
+			if (!system->nr_events) {
+				system = NULL;
+				break;
+			}
+			__get_system(system);
+			break;
+		}
+	}
+	mutex_unlock(&event_mutex);
+
+	if (system != inode->i_private)
+		return -ENODEV;
+
+	ret = tracing_open_generic(inode, filp);
+	if (ret < 0)
+		put_system(system);
+
+	return ret;
+}
+
+static int subsystem_release(struct inode *inode, struct file *file)
+{
+	struct event_subsystem *system = inode->i_private;
+
+	put_system(system);
+
+	return 0;
+}
+
 static ssize_t
 subsystem_filter_read(struct file *filp, char __user *ubuf, size_t cnt,
 		      loff_t *ppos)
@@ -963,10 +1033,11 @@ static const struct file_operations ftrace_event_filter_fops = {
 };
 
 static const struct file_operations ftrace_subsystem_filter_fops = {
-	.open = tracing_open_generic,
+	.open = subsystem_open,
 	.read = subsystem_filter_read,
 	.write = subsystem_filter_write,
 	.llseek = default_llseek,
+	.release = subsystem_release,
 };
 
 static const struct file_operations ftrace_system_enable_fops = {
@@ -1002,8 +1073,6 @@ static struct dentry *event_trace_events_dir(void)
 	return d_events;
 }
 
-static LIST_HEAD(event_subsystems);
-
 static struct dentry *
 event_subsystem_dir(const char *name, struct dentry *d_events)
 {
@@ -1013,6 +1082,7 @@ event_subsystem_dir(const char *name, struct dentry *d_events)
 	/* First see if we did not already create this dir */
 	list_for_each_entry(system, &event_subsystems, list) {
 		if (strcmp(system->name, name) == 0) {
+			__get_system(system);
 			system->nr_events++;
 			return system->entry;
 		}
@@ -1035,6 +1105,7 @@ event_subsystem_dir(const char *name, struct dentry *d_events)
 	}
 
 	system->nr_events = 1;
+	system->ref_count = 1;
 	system->name = kstrdup(name, GFP_KERNEL);
 	if (!system->name) {
 		debugfs_remove(system->entry);
@@ -1184,16 +1255,9 @@ static void remove_subsystem_dir(const char *name)
 	list_for_each_entry(system, &event_subsystems, list) {
 		if (strcmp(system->name, name) == 0) {
 			if (!--system->nr_events) {
-				struct event_filter *filter = system->filter;
-
 				debugfs_remove_recursive(system->entry);
 				list_del(&system->list);
-				if (filter) {
-					kfree(filter->filter_string);
-					kfree(filter);
-				}
-				kfree(system->name);
-				kfree(system);
+				__put_system(system);
 			}
 			break;
 		}
diff --git a/kernel/trace/trace_events_filter.c b/kernel/trace/trace_events_filter.c
index 8008ddc..256764e 100644
--- a/kernel/trace/trace_events_filter.c
+++ b/kernel/trace/trace_events_filter.c
@@ -1886,6 +1886,12 @@ int apply_subsystem_event_filter(struct event_subsystem *system,
 
 	mutex_lock(&event_mutex);
 
+	/* Make sure the system still has events */
+	if (!system->nr_events) {
+		err = -ENODEV;
+		goto out_unlock;
+	}
+
 	if (!strcmp(strstrip(filter_string), "0")) {
 		filter_free_subsystem_preds(system);
 		remove_filter_string(system->filter);
-- 
1.7.5.4



^ permalink raw reply related	[flat|nested] 5+ messages in thread

* [PATCH 2/2] tracing: Have enable system events use the subsystem_open routine
  2011-07-06 21:19 [PATCH 0/2] [GIT PULL][v3.0] tracing: Add refcount for system filter and enable Steven Rostedt
  2011-07-06 21:19 ` [PATCH 1/2] tracing: Add ref_count to event systems for freeing Steven Rostedt
@ 2011-07-06 21:19 ` Steven Rostedt
  2011-07-07 14:15 ` [PATCH 0/2] [GIT PULL][v3.0] tracing: Add refcount for system filter and enable Ingo Molnar
  2 siblings, 0 replies; 5+ messages in thread
From: Steven Rostedt @ 2011-07-06 21:19 UTC (permalink / raw)
  To: linux-kernel
  Cc: Ingo Molnar, Andrew Morton, Frederic Weisbecker, stable, Johannes Berg

[-- Attachment #1: 0002-tracing-Have-enable-system-events-use-the-subsystem_.patch --]
[-- Type: text/plain, Size: 3767 bytes --]

From: Steven Rostedt <srostedt@redhat.com>

As the system name can be freed when a module is removed, the reference
to the system name that is passed to the enable routines to enable
events at a system level can point to arbitrary memory. This value is
only read, but it will just read garbage.

Change the system event enabling to use the subsystem_open routines
like the system filter routines do.

Cc: <stable@kernel.org>
Reported-by: Johannes Berg <johannes.berg@intel.com>
Signed-off-by: Steven Rostedt <rostedt@goodmis.org>
---
 kernel/trace/trace_events.c |   31 ++++++++++++++++++++++---------
 1 files changed, 22 insertions(+), 9 deletions(-)

diff --git a/kernel/trace/trace_events.c b/kernel/trace/trace_events.c
index ffc5b28..3e2a7c9 100644
--- a/kernel/trace/trace_events.c
+++ b/kernel/trace/trace_events.c
@@ -557,7 +557,7 @@ system_enable_read(struct file *filp, char __user *ubuf, size_t cnt,
 		   loff_t *ppos)
 {
 	const char set_to_char[4] = { '?', '0', '1', 'X' };
-	const char *system = filp->private_data;
+	struct event_subsystem *system = filp->private_data;
 	struct ftrace_event_call *call;
 	char buf[2];
 	int set = 0;
@@ -568,7 +568,7 @@ system_enable_read(struct file *filp, char __user *ubuf, size_t cnt,
 		if (!call->name || !call->class || !call->class->reg)
 			continue;
 
-		if (system && strcmp(call->class->system, system) != 0)
+		if (system && strcmp(call->class->system, system->name) != 0)
 			continue;
 
 		/*
@@ -598,7 +598,8 @@ static ssize_t
 system_enable_write(struct file *filp, const char __user *ubuf, size_t cnt,
 		    loff_t *ppos)
 {
-	const char *system = filp->private_data;
+	struct event_subsystem *system = filp->private_data;
+	const char *name = NULL;
 	unsigned long val;
 	char buf[64];
 	ssize_t ret;
@@ -622,7 +623,14 @@ system_enable_write(struct file *filp, const char __user *ubuf, size_t cnt,
 	if (val != 0 && val != 1)
 		return -EINVAL;
 
-	ret = __ftrace_set_clr_event(NULL, system, NULL, val);
+	/*
+	 * Opening of "enable" adds a ref count to system,
+	 * so the name is safe to use.
+	 */
+	if (system)
+		name = system->name;
+
+	ret = __ftrace_set_clr_event(NULL, name, NULL, val);
 	if (ret)
 		goto out;
 
@@ -862,6 +870,9 @@ static int subsystem_open(struct inode *inode, struct file *filp)
 	struct event_subsystem *system = NULL;
 	int ret;
 
+	if (!inode->i_private)
+		goto skip_search;
+
 	/* Make sure the system still exists */
 	mutex_lock(&event_mutex);
 	list_for_each_entry(system, &event_subsystems, list) {
@@ -880,8 +891,9 @@ static int subsystem_open(struct inode *inode, struct file *filp)
 	if (system != inode->i_private)
 		return -ENODEV;
 
+ skip_search:
 	ret = tracing_open_generic(inode, filp);
-	if (ret < 0)
+	if (ret < 0 && system)
 		put_system(system);
 
 	return ret;
@@ -891,7 +903,8 @@ static int subsystem_release(struct inode *inode, struct file *file)
 {
 	struct event_subsystem *system = inode->i_private;
 
-	put_system(system);
+	if (system)
+		put_system(system);
 
 	return 0;
 }
@@ -1041,10 +1054,11 @@ static const struct file_operations ftrace_subsystem_filter_fops = {
 };
 
 static const struct file_operations ftrace_system_enable_fops = {
-	.open = tracing_open_generic,
+	.open = subsystem_open,
 	.read = system_enable_read,
 	.write = system_enable_write,
 	.llseek = default_llseek,
+	.release = subsystem_release,
 };
 
 static const struct file_operations ftrace_show_header_fops = {
@@ -1133,8 +1147,7 @@ event_subsystem_dir(const char *name, struct dentry *d_events)
 			   "'%s/filter' entry\n", name);
 	}
 
-	trace_create_file("enable", 0644, system->entry,
-			  (void *)system->name,
+	trace_create_file("enable", 0644, system->entry, system,
 			  &ftrace_system_enable_fops);
 
 	return system->entry;
-- 
1.7.5.4



^ permalink raw reply related	[flat|nested] 5+ messages in thread

* Re: [PATCH 0/2] [GIT PULL][v3.0] tracing: Add refcount for system filter and enable
  2011-07-06 21:19 [PATCH 0/2] [GIT PULL][v3.0] tracing: Add refcount for system filter and enable Steven Rostedt
  2011-07-06 21:19 ` [PATCH 1/2] tracing: Add ref_count to event systems for freeing Steven Rostedt
  2011-07-06 21:19 ` [PATCH 2/2] tracing: Have enable system events use the subsystem_open routine Steven Rostedt
@ 2011-07-07 14:15 ` Ingo Molnar
  2011-07-07 14:23   ` Steven Rostedt
  2 siblings, 1 reply; 5+ messages in thread
From: Ingo Molnar @ 2011-07-07 14:15 UTC (permalink / raw)
  To: Steven Rostedt; +Cc: linux-kernel, Andrew Morton, Frederic Weisbecker


* Steven Rostedt <rostedt@goodmis.org> wrote:

> 
> Ingo,
> 
> Please pull the latest tip/perf/urgent tree, which can be found at:
> 
>   git://git.kernel.org/pub/scm/linux/kernel/git/rostedt/linux-2.6-trace.git
> tip/perf/urgent
> 
> Head SHA1: 57f124ca21d216fca3ba3995235c15855f9b9980
> 
> 
> Steven Rostedt (2):
>       tracing: Add ref_count to event systems for freeing
>       tracing: Have enable system events use the subsystem_open routine
> 
> ----
>  kernel/trace/trace.h               |    1 +
>  kernel/trace/trace_events.c        |  113 ++++++++++++++++++++++++++++++------
>  kernel/trace/trace_events_filter.c |    6 ++
>  3 files changed, 102 insertions(+), 18 deletions(-)

Hm, i'm wondering, when was this bug introduced?

Thanks,

	Ingo

^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: [PATCH 0/2] [GIT PULL][v3.0] tracing: Add refcount for system filter and enable
  2011-07-07 14:15 ` [PATCH 0/2] [GIT PULL][v3.0] tracing: Add refcount for system filter and enable Ingo Molnar
@ 2011-07-07 14:23   ` Steven Rostedt
  0 siblings, 0 replies; 5+ messages in thread
From: Steven Rostedt @ 2011-07-07 14:23 UTC (permalink / raw)
  To: Ingo Molnar; +Cc: linux-kernel, Andrew Morton, Frederic Weisbecker

On Thu, 2011-07-07 at 16:15 +0200, Ingo Molnar wrote:
> * Steven Rostedt <rostedt@goodmis.org> wrote:
> 
> > 
> > Ingo,
> > 
> > Please pull the latest tip/perf/urgent tree, which can be found at:
> > 
> >   git://git.kernel.org/pub/scm/linux/kernel/git/rostedt/linux-2.6-trace.git
> > tip/perf/urgent
> > 
> > Head SHA1: 57f124ca21d216fca3ba3995235c15855f9b9980
> > 
> > 
> > Steven Rostedt (2):
> >       tracing: Add ref_count to event systems for freeing
> >       tracing: Have enable system events use the subsystem_open routine
> > 
> > ----
> >  kernel/trace/trace.h               |    1 +
> >  kernel/trace/trace_events.c        |  113 ++++++++++++++++++++++++++++++------
> >  kernel/trace/trace_events_filter.c |    6 ++
> >  3 files changed, 102 insertions(+), 18 deletions(-)
> 
> Hm, i'm wondering, when was this bug introduced?
> 

It was introduced a while ago, I believe by this commit:

commit dc82ec98a4727fd51b77e92d05fe7d2db3dcc11c
Date:   Thu Jul 9 16:22:22 2009 +0800
tracing/filter: Remove empty subsystem and its directory


-- Steve



^ permalink raw reply	[flat|nested] 5+ messages in thread

end of thread, other threads:[~2011-07-07 14:23 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2011-07-06 21:19 [PATCH 0/2] [GIT PULL][v3.0] tracing: Add refcount for system filter and enable Steven Rostedt
2011-07-06 21:19 ` [PATCH 1/2] tracing: Add ref_count to event systems for freeing Steven Rostedt
2011-07-06 21:19 ` [PATCH 2/2] tracing: Have enable system events use the subsystem_open routine Steven Rostedt
2011-07-07 14:15 ` [PATCH 0/2] [GIT PULL][v3.0] tracing: Add refcount for system filter and enable Ingo Molnar
2011-07-07 14:23   ` Steven Rostedt

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).