deal with races in /proc/*/{syscall, stack, personality}, CVE-2011-1020
authorAl Viro <viro@zeniv.linux.org.uk>
Thu, 21 Jul 2011 13:13:46 +0000 (14:13 +0100)
committerHerton Ronaldo Krzesinski <herton.krzesinski@canonical.com>
Mon, 29 Aug 2011 19:23:07 +0000 (16:23 -0300)
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>
Acked-by: Stefan Bader <stefan.bader@canonical.com>
Signed-off-by: Tim Gardner <tim.gardner@canonical.com>

fs/proc/base.c

index bf64fd7..2c0d6f5 100644 (file)
@@ -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),