CVE-2013-3301: Other (NVD-CWE-Other)
value: The ftrace implementation in the Linux kernel before 3.8.8 allows local users to cause a denial of service (NULL pointer dereference and system crash) or possibly have unspecified other impact by leveraging the CAP_SYS_ADMIN capability for write access to the (1) set_ftrace_pid or (2) set_graph_function file, and then making an lseek system call.
CWE description: NVD is only using a subset of CWE for mapping instead of the entire CWE, and the weakness type is not covered by that subset.
Published: 2013-04-29T14:55Z
Repository: https://github.com/torvalds/linux
Language: C
Files changed: include/linux/ftrace.h, kernel/trace/ftrace.c, kernel/trace/trace_stack.c
Commit date: 2013-04-12 14:43:34 -0400
Commit message: tracing: Fix possible NULL pointer dereferences

Currently set_ftrace_pid and set_graph_function files use seq_lseek
for their fops.  However seq_open() is called only for FMODE_READ in
the fops->open() so that if an user tries to seek one of those file
when she open it for writing, it sees NULL seq_file and then panic.

It can be easily reproduced with following command:

  $ cd /sys/kernel/debug/tracing
  $ echo 1234 | sudo tee -a set_ftrace_pid

In this example, GNU coreutils' tee opens the file with fopen(, "a")
and then the fopen() internally calls lseek().

Link: http://lkml.kernel.org/r/1365663302-2170-1-git-send-email-namhyung@kernel.org

Cc: Frederic Weisbecker <fweisbec@gmail.com>
Cc: Ingo Molnar <mingo@kernel.org>
Cc: Namhyung Kim <namhyung.kim@lge.com>
Cc: stable@vger.kernel.org
Signed-off-by: Namhyung Kim <namhyung@kernel.org>
Signed-off-by: Steven Rostedt <rostedt@goodmis.org>

Diff: diff --git a/include/linux/ftrace.h b/include/linux/ftrace.h
index 167abf9078020a..eb3ce327b9753c 100644
--- a/include/linux/ftrace.h
+++ b/include/linux/ftrace.h
@@ -394,11 +394,11 @@ int ftrace_regex_open(struct ftrace_ops *ops, int flag,
 		  struct inode *inode, struct file *file);
 ssize_t ftrace_filter_write(struct file *file, const char __user *ubuf,
 			    size_t cnt, loff_t *ppos);
 ssize_t ftrace_notrace_write(struct file *file, const char __user *ubuf,
 			     size_t cnt, loff_t *ppos);
-loff_t ftrace_regex_lseek(struct file *file, loff_t offset, int whence);
+loff_t ftrace_filter_lseek(struct file *file, loff_t offset, int whence);
 int ftrace_regex_release(struct inode *inode, struct file *file);
 
 void __init
 ftrace_set_early_filter(struct ftrace_ops *ops, char *buf, int enable);
 
diff --git a/kernel/trace/ftrace.c b/kernel/trace/ftrace.c
index 926ebfb7493638..affc35d829cc94 100644
--- a/kernel/trace/ftrace.c
+++ b/kernel/trace/ftrace.c
@@ -2695,11 +2695,11 @@ ftrace_notrace_open(struct inode *inode, struct file *file)
 	return ftrace_regex_open(&global_ops, FTRACE_ITER_NOTRACE,
 				 inode, file);
 }
 
 loff_t
-ftrace_regex_lseek(struct file *file, loff_t offset, int whence)
+ftrace_filter_lseek(struct file *file, loff_t offset, int whence)
 {
 	loff_t ret;
 
 	if (file->f_mode & FMODE_READ)
 		ret = seq_lseek(file, offset, whence);
@@ -3568,19 +3568,19 @@ static const struct file_operations ftrace_enabled_fops = {
 
 static const struct file_operations ftrace_filter_fops = {
 	.open = ftrace_filter_open,
 	.read = seq_read,
 	.write = ftrace_filter_write,
-	.llseek = ftrace_regex_lseek,
+	.llseek = ftrace_filter_lseek,
 	.release = ftrace_regex_release,
 };
 
 static const struct file_operations ftrace_notrace_fops = {
 	.open = ftrace_notrace_open,
 	.read = seq_read,
 	.write = ftrace_notrace_write,
-	.llseek = ftrace_regex_lseek,
+	.llseek = ftrace_filter_lseek,
 	.release = ftrace_regex_release,
 };
 
 #ifdef CONFIG_FUNCTION_GRAPH_TRACER
 
@@ -3781,12 +3781,12 @@ ftrace_graph_write(struct file *file, const char __user *ubuf,
 
 static const struct file_operations ftrace_graph_fops = {
 	.open		= ftrace_graph_open,
 	.read		= seq_read,
 	.write		= ftrace_graph_write,
+	.llseek		= ftrace_filter_lseek,
 	.release	= ftrace_graph_release,
-	.llseek		= seq_lseek,
 };
 #endif /* CONFIG_FUNCTION_GRAPH_TRACER */
 
 static __init int ftrace_init_dyn_debugfs(struct dentry *d_tracer)
 {
@@ -4437,11 +4437,11 @@ ftrace_pid_release(struct inode *inode, struct file *file)
 
 static const struct file_operations ftrace_pid_fops = {
 	.open		= ftrace_pid_open,
 	.write		= ftrace_pid_write,
 	.read		= seq_read,
-	.llseek		= seq_lseek,
+	.llseek		= ftrace_filter_lseek,
 	.release	= ftrace_pid_release,
 };
 
 static __init int ftrace_init_debugfs(void)
 {
diff --git a/kernel/trace/trace_stack.c b/kernel/trace/trace_stack.c
index 42ca822fc70196..83a8b5b7bd35fa 100644
--- a/kernel/trace/trace_stack.c
+++ b/kernel/trace/trace_stack.c
@@ -320,11 +320,11 @@ stack_trace_filter_open(struct inode *inode, struct file *file)
 
 static const struct file_operations stack_trace_filter_fops = {
 	.open = stack_trace_filter_open,
 	.read = seq_read,
 	.write = ftrace_filter_write,
-	.llseek = ftrace_regex_lseek,
+	.llseek = ftrace_filter_lseek,
 	.release = ftrace_regex_release,
 };
 
 int
 stack_trace_sysctl(struct ctl_table *table, int write,

Vulnerable code: loff_t ftrace_regex_lseek(struct file *file, loff_t offset, int whence);
ftrace_regex_lseek(struct file *file, loff_t offset, int whence)
	.llseek = ftrace_regex_lseek,
	.llseek = ftrace_regex_lseek,
	.llseek		= seq_lseek,
	.llseek		= seq_lseek,
	.llseek = ftrace_regex_lseek,
Fixed code: loff_t ftrace_filter_lseek(struct file *file, loff_t offset, int whence);
ftrace_filter_lseek(struct file *file, loff_t offset, int whence)
	.llseek = ftrace_filter_lseek,
	.llseek = ftrace_filter_lseek,
	.llseek		= ftrace_filter_lseek,
	.llseek		= ftrace_filter_lseek,
	.llseek = ftrace_filter_lseek,