Patchwork [natty,natty/ti-omap4,CVE,5/5] deal with races in /proc/*/{syscall, stack, personality}

login
register
mail settings
Submitter Andy Whitcroft
Date July 21, 2011, 1:13 p.m.
Message ID <1311254026-29719-17-git-send-email-apw@canonical.com>
Download mbox | patch
Permalink /patch/106053/
State New
Headers show

Comments

Andy Whitcroft - July 21, 2011, 1:13 p.m.
From: Al Viro <viro@zeniv.linux.org.uk>

All of those are rw-r--r-- and all are broken for suid - if you open
a file before the target does suid-root exec, you'll be still able
to access it.  For personality it's not a big deal, but for syscall
and stack it's a real problem.

Fix: check that task is tracable for you at the time of read().

Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>

(backported from commit a9712bc12c40c172e393f85a9b2ba8db4bf59509)
CVE-2011-1020
BugLink: http://bugs.launchpad.net/bugs/813026
Signed-off-by: Andy Whitcroft <apw@canonical.com>
---
 fs/proc/base.c |   69 ++++++++++++++++++++++++++++++++++++++++---------------
 1 files changed, 50 insertions(+), 19 deletions(-)
Tim Gardner - July 21, 2011, 8:09 p.m.
On 07/21/2011 06:13 AM, Andy Whitcroft wrote:
> From: Al Viro<viro@zeniv.linux.org.uk>
>
> All of those are rw-r--r-- and all are broken for suid - if you open
> a file before the target does suid-root exec, you'll be still able
> to access it.  For personality it's not a big deal, but for syscall
> and stack it's a real problem.
>
> Fix: check that task is tracable for you at the time of read().
>
> Signed-off-by: Al Viro<viro@zeniv.linux.org.uk>
>
> (backported from commit a9712bc12c40c172e393f85a9b2ba8db4bf59509)
> CVE-2011-1020
> BugLink: http://bugs.launchpad.net/bugs/813026
> Signed-off-by: Andy Whitcroft<apw@canonical.com>
> ---
>   fs/proc/base.c |   69 ++++++++++++++++++++++++++++++++++++++++---------------
>   1 files changed, 50 insertions(+), 19 deletions(-)
>
> diff --git a/fs/proc/base.c b/fs/proc/base.c
> index bf64fd7..2c0d6f5 100644
> --- a/fs/proc/base.c
> +++ b/fs/proc/base.c
> @@ -320,6 +320,23 @@ static int proc_pid_wchan(struct task_struct *task, char *buffer)
>   }
>   #endif /* CONFIG_KALLSYMS */
>
> +static int lock_trace(struct task_struct *task)
> +{
> +	int err = mutex_lock_killable(&task->signal->cred_guard_mutex);
> +	if (err)
> +		return err;
> +	if (!ptrace_may_access(task, PTRACE_MODE_ATTACH)) {
> +		mutex_unlock(&task->signal->cred_guard_mutex);
> +		return -EPERM;
> +	}
> +	return 0;
> +}
> +
> +static void unlock_trace(struct task_struct *task)
> +{
> +	mutex_unlock(&task->signal->cred_guard_mutex);
> +}
> +
>   #ifdef CONFIG_STACKTRACE
>
>   #define MAX_STACK_TRACE_DEPTH	64
> @@ -329,6 +346,7 @@ static int proc_pid_stack(struct seq_file *m, struct pid_namespace *ns,
>   {
>   	struct stack_trace trace;
>   	unsigned long *entries;
> +	int err;
>   	int i;
>
>   	entries = kmalloc(MAX_STACK_TRACE_DEPTH * sizeof(*entries), GFP_KERNEL);
> @@ -339,15 +357,20 @@ static int proc_pid_stack(struct seq_file *m, struct pid_namespace *ns,
>   	trace.max_entries	= MAX_STACK_TRACE_DEPTH;
>   	trace.entries		= entries;
>   	trace.skip		= 0;
> -	save_stack_trace_tsk(task,&trace);
>
> -	for (i = 0; i<  trace.nr_entries; i++) {
> -		seq_printf(m, "[<%pK>] %pS\n",
> -			   (void *)entries[i], (void *)entries[i]);
> +	err = lock_trace(task);
> +	if (!err) {
> +		save_stack_trace_tsk(task,&trace);
> +
> +		for (i = 0; i<  trace.nr_entries; i++) {
> +			seq_printf(m, "[<%pK>] %pS\n",
> +				   (void *)entries[i], (void *)entries[i]);
> +		}
> +		unlock_trace(task);
>   	}
>   	kfree(entries);
>
> -	return 0;
> +	return err;
>   }
>   #endif
>
> @@ -510,18 +533,22 @@ static int proc_pid_syscall(struct task_struct *task, char *buffer)
>   {
>   	long nr;
>   	unsigned long args[6], sp, pc;
> +	int res = lock_trace(task);
> +	if (res)
> +		return res;
>
>   	if (task_current_syscall(task,&nr, args, 6,&sp,&pc))
> -		return sprintf(buffer, "running\n");
> -
> -	if (nr<  0)
> -		return sprintf(buffer, "%ld 0x%lx 0x%lx\n", nr, sp, pc);
> -
> -	return sprintf(buffer,
> +		res = sprintf(buffer, "running\n");
> +	else if (nr<  0)
> +		res = sprintf(buffer, "%ld 0x%lx 0x%lx\n", nr, sp, pc);
> +	else
> +		res = sprintf(buffer,
>   		       "%ld 0x%lx 0x%lx 0x%lx 0x%lx 0x%lx 0x%lx 0x%lx 0x%lx\n",
>   		       nr,
>   		       args[0], args[1], args[2], args[3], args[4], args[5],
>   		       sp, pc);
> +	unlock_trace(task);
> +	return res;
>   }
>   #endif /* CONFIG_HAVE_ARCH_TRACEHOOK */
>
> @@ -2748,8 +2775,12 @@ static int proc_tgid_io_accounting(struct task_struct *task, char *buffer)
>   static int proc_pid_personality(struct seq_file *m, struct pid_namespace *ns,
>   				struct pid *pid, struct task_struct *task)
>   {
> -	seq_printf(m, "%08x\n", task->personality);
> -	return 0;
> +	int err = lock_trace(task);
> +	if (!err) {
> +		seq_printf(m, "%08x\n", task->personality);
> +		unlock_trace(task);
> +	}
> +	return err;
>   }
>
>   /*
> @@ -2768,7 +2799,7 @@ static const struct pid_entry tgid_base_stuff[] = {
>   	REG("environ",    S_IRUSR, proc_environ_operations),
>   	INF("auxv",       S_IRUSR, proc_pid_auxv),
>   	ONE("status",     S_IRUGO, proc_pid_status),
> -	ONE("personality", S_IRUSR, proc_pid_personality),
> +	ONE("personality", S_IRUGO, proc_pid_personality),
>   	INF("limits",	  S_IRUGO, proc_pid_limits),
>   #ifdef CONFIG_SCHED_DEBUG
>   	REG("sched",      S_IRUGO|S_IWUSR, proc_pid_sched_operations),
> @@ -2778,7 +2809,7 @@ static const struct pid_entry tgid_base_stuff[] = {
>   #endif
>   	REG("comm",      S_IRUGO|S_IWUSR, proc_pid_set_comm_operations),
>   #ifdef CONFIG_HAVE_ARCH_TRACEHOOK
> -	INF("syscall",    S_IRUSR, proc_pid_syscall),
> +	INF("syscall",    S_IRUGO, proc_pid_syscall),
>   #endif
>   	INF("cmdline",    S_IRUGO, proc_pid_cmdline),
>   	ONE("stat",       S_IRUGO, proc_tgid_stat),
> @@ -2806,7 +2837,7 @@ static const struct pid_entry tgid_base_stuff[] = {
>   	INF("wchan",      S_IRUGO, proc_pid_wchan),
>   #endif
>   #ifdef CONFIG_STACKTRACE
> -	ONE("stack",      S_IRUSR, proc_pid_stack),
> +	ONE("stack",      S_IRUGO, proc_pid_stack),
>   #endif
>   #ifdef CONFIG_SCHEDSTATS
>   	INF("schedstat",  S_IRUGO, proc_pid_schedstat),
> @@ -3113,14 +3144,14 @@ static const struct pid_entry tid_base_stuff[] = {
>   	REG("environ",   S_IRUSR, proc_environ_operations),
>   	INF("auxv",      S_IRUSR, proc_pid_auxv),
>   	ONE("status",    S_IRUGO, proc_pid_status),
> -	ONE("personality", S_IRUSR, proc_pid_personality),
> +	ONE("personality", S_IRUGO, proc_pid_personality),
>   	INF("limits",	 S_IRUGO, proc_pid_limits),
>   #ifdef CONFIG_SCHED_DEBUG
>   	REG("sched",     S_IRUGO|S_IWUSR, proc_pid_sched_operations),
>   #endif
>   	REG("comm",      S_IRUGO|S_IWUSR, proc_pid_set_comm_operations),
>   #ifdef CONFIG_HAVE_ARCH_TRACEHOOK
> -	INF("syscall",   S_IRUSR, proc_pid_syscall),
> +	INF("syscall",   S_IRUGO, proc_pid_syscall),
>   #endif
>   	INF("cmdline",   S_IRUGO, proc_pid_cmdline),
>   	ONE("stat",      S_IRUGO, proc_tid_stat),
> @@ -3147,7 +3178,7 @@ static const struct pid_entry tid_base_stuff[] = {
>   	INF("wchan",     S_IRUGO, proc_pid_wchan),
>   #endif
>   #ifdef CONFIG_STACKTRACE
> -	ONE("stack",      S_IRUSR, proc_pid_stack),
> +	ONE("stack",      S_IRUGO, proc_pid_stack),
>   #endif
>   #ifdef CONFIG_SCHEDSTATS
>   	INF("schedstat", S_IRUGO, proc_pid_schedstat),

Patch

diff --git a/fs/proc/base.c b/fs/proc/base.c
index bf64fd7..2c0d6f5 100644
--- a/fs/proc/base.c
+++ b/fs/proc/base.c
@@ -320,6 +320,23 @@  static int proc_pid_wchan(struct task_struct *task, char *buffer)
 }
 #endif /* CONFIG_KALLSYMS */
 
+static int lock_trace(struct task_struct *task)
+{
+	int err = mutex_lock_killable(&task->signal->cred_guard_mutex);
+	if (err)
+		return err;
+	if (!ptrace_may_access(task, PTRACE_MODE_ATTACH)) {
+		mutex_unlock(&task->signal->cred_guard_mutex);
+		return -EPERM;
+	}
+	return 0;
+}
+
+static void unlock_trace(struct task_struct *task)
+{
+	mutex_unlock(&task->signal->cred_guard_mutex);
+}
+
 #ifdef CONFIG_STACKTRACE
 
 #define MAX_STACK_TRACE_DEPTH	64
@@ -329,6 +346,7 @@  static int proc_pid_stack(struct seq_file *m, struct pid_namespace *ns,
 {
 	struct stack_trace trace;
 	unsigned long *entries;
+	int err;
 	int i;
 
 	entries = kmalloc(MAX_STACK_TRACE_DEPTH * sizeof(*entries), GFP_KERNEL);
@@ -339,15 +357,20 @@  static int proc_pid_stack(struct seq_file *m, struct pid_namespace *ns,
 	trace.max_entries	= MAX_STACK_TRACE_DEPTH;
 	trace.entries		= entries;
 	trace.skip		= 0;
-	save_stack_trace_tsk(task, &trace);
 
-	for (i = 0; i < trace.nr_entries; i++) {
-		seq_printf(m, "[<%pK>] %pS\n",
-			   (void *)entries[i], (void *)entries[i]);
+	err = lock_trace(task);
+	if (!err) {
+		save_stack_trace_tsk(task, &trace);
+
+		for (i = 0; i < trace.nr_entries; i++) {
+			seq_printf(m, "[<%pK>] %pS\n",
+				   (void *)entries[i], (void *)entries[i]);
+		}
+		unlock_trace(task);
 	}
 	kfree(entries);
 
-	return 0;
+	return err;
 }
 #endif
 
@@ -510,18 +533,22 @@  static int proc_pid_syscall(struct task_struct *task, char *buffer)
 {
 	long nr;
 	unsigned long args[6], sp, pc;
+	int res = lock_trace(task);
+	if (res)
+		return res;
 
 	if (task_current_syscall(task, &nr, args, 6, &sp, &pc))
-		return sprintf(buffer, "running\n");
-
-	if (nr < 0)
-		return sprintf(buffer, "%ld 0x%lx 0x%lx\n", nr, sp, pc);
-
-	return sprintf(buffer,
+		res = sprintf(buffer, "running\n");
+	else if (nr < 0)
+		res = sprintf(buffer, "%ld 0x%lx 0x%lx\n", nr, sp, pc);
+	else
+		res = sprintf(buffer,
 		       "%ld 0x%lx 0x%lx 0x%lx 0x%lx 0x%lx 0x%lx 0x%lx 0x%lx\n",
 		       nr,
 		       args[0], args[1], args[2], args[3], args[4], args[5],
 		       sp, pc);
+	unlock_trace(task);
+	return res;
 }
 #endif /* CONFIG_HAVE_ARCH_TRACEHOOK */
 
@@ -2748,8 +2775,12 @@  static int proc_tgid_io_accounting(struct task_struct *task, char *buffer)
 static int proc_pid_personality(struct seq_file *m, struct pid_namespace *ns,
 				struct pid *pid, struct task_struct *task)
 {
-	seq_printf(m, "%08x\n", task->personality);
-	return 0;
+	int err = lock_trace(task);
+	if (!err) {
+		seq_printf(m, "%08x\n", task->personality);
+		unlock_trace(task);
+	}
+	return err;
 }
 
 /*
@@ -2768,7 +2799,7 @@  static const struct pid_entry tgid_base_stuff[] = {
 	REG("environ",    S_IRUSR, proc_environ_operations),
 	INF("auxv",       S_IRUSR, proc_pid_auxv),
 	ONE("status",     S_IRUGO, proc_pid_status),
-	ONE("personality", S_IRUSR, proc_pid_personality),
+	ONE("personality", S_IRUGO, proc_pid_personality),
 	INF("limits",	  S_IRUGO, proc_pid_limits),
 #ifdef CONFIG_SCHED_DEBUG
 	REG("sched",      S_IRUGO|S_IWUSR, proc_pid_sched_operations),
@@ -2778,7 +2809,7 @@  static const struct pid_entry tgid_base_stuff[] = {
 #endif
 	REG("comm",      S_IRUGO|S_IWUSR, proc_pid_set_comm_operations),
 #ifdef CONFIG_HAVE_ARCH_TRACEHOOK
-	INF("syscall",    S_IRUSR, proc_pid_syscall),
+	INF("syscall",    S_IRUGO, proc_pid_syscall),
 #endif
 	INF("cmdline",    S_IRUGO, proc_pid_cmdline),
 	ONE("stat",       S_IRUGO, proc_tgid_stat),
@@ -2806,7 +2837,7 @@  static const struct pid_entry tgid_base_stuff[] = {
 	INF("wchan",      S_IRUGO, proc_pid_wchan),
 #endif
 #ifdef CONFIG_STACKTRACE
-	ONE("stack",      S_IRUSR, proc_pid_stack),
+	ONE("stack",      S_IRUGO, proc_pid_stack),
 #endif
 #ifdef CONFIG_SCHEDSTATS
 	INF("schedstat",  S_IRUGO, proc_pid_schedstat),
@@ -3113,14 +3144,14 @@  static const struct pid_entry tid_base_stuff[] = {
 	REG("environ",   S_IRUSR, proc_environ_operations),
 	INF("auxv",      S_IRUSR, proc_pid_auxv),
 	ONE("status",    S_IRUGO, proc_pid_status),
-	ONE("personality", S_IRUSR, proc_pid_personality),
+	ONE("personality", S_IRUGO, proc_pid_personality),
 	INF("limits",	 S_IRUGO, proc_pid_limits),
 #ifdef CONFIG_SCHED_DEBUG
 	REG("sched",     S_IRUGO|S_IWUSR, proc_pid_sched_operations),
 #endif
 	REG("comm",      S_IRUGO|S_IWUSR, proc_pid_set_comm_operations),
 #ifdef CONFIG_HAVE_ARCH_TRACEHOOK
-	INF("syscall",   S_IRUSR, proc_pid_syscall),
+	INF("syscall",   S_IRUGO, proc_pid_syscall),
 #endif
 	INF("cmdline",   S_IRUGO, proc_pid_cmdline),
 	ONE("stat",      S_IRUGO, proc_tid_stat),
@@ -3147,7 +3178,7 @@  static const struct pid_entry tid_base_stuff[] = {
 	INF("wchan",     S_IRUGO, proc_pid_wchan),
 #endif
 #ifdef CONFIG_STACKTRACE
-	ONE("stack",      S_IRUSR, proc_pid_stack),
+	ONE("stack",      S_IRUGO, proc_pid_stack),
 #endif
 #ifdef CONFIG_SCHEDSTATS
 	INF("schedstat", S_IRUGO, proc_pid_schedstat),