aboutsummaryrefslogtreecommitdiffstats
path: root/fs/proc/base.c
diff options
context:
space:
mode:
authorVasiliy Kulikov <segoon@openwall.com>2011-07-26 19:08:38 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2011-07-26 19:49:43 -0400
commit293eb1e7772b25a93647c798c7b89bf26c2da2e0 (patch)
treedebd49012773091732ee1e1b2388462b759525a7 /fs/proc/base.c
parentd2857e79a2ba7c155eaa1a7d3581c8d26b31e54e (diff)
proc: fix a race in do_io_accounting()
If an inode's mode permits opening /proc/PID/io and the resulting file descriptor is kept across execve() of a setuid or similar binary, the ptrace_may_access() check tries to prevent using this fd against the task with escalated privileges. Unfortunately, there is a race in the check against execve(). If execve() is processed after the ptrace check, but before the actual io information gathering, io statistics will be gathered from the privileged process. At least in theory this might lead to gathering sensible information (like ssh/ftp password length) that wouldn't be available otherwise. Holding task->signal->cred_guard_mutex while gathering the io information should protect against the race. The order of locking is similar to the one inside of ptrace_attach(): first goes cred_guard_mutex, then lock_task_sighand(). Signed-off-by: Vasiliy Kulikov <segoon@openwall.com> Cc: Al Viro <viro@zeniv.linux.org.uk> Cc: <stable@kernel.org> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'fs/proc/base.c')
-rw-r--r--fs/proc/base.c16
1 files changed, 13 insertions, 3 deletions
diff --git a/fs/proc/base.c b/fs/proc/base.c
index c9e3f650f23c..08e3eccf9a12 100644
--- a/fs/proc/base.c
+++ b/fs/proc/base.c
@@ -2706,9 +2706,16 @@ static int do_io_accounting(struct task_struct *task, char *buffer, int whole)
2706{ 2706{
2707 struct task_io_accounting acct = task->ioac; 2707 struct task_io_accounting acct = task->ioac;
2708 unsigned long flags; 2708 unsigned long flags;
2709 int result;
2709 2710
2710 if (!ptrace_may_access(task, PTRACE_MODE_READ)) 2711 result = mutex_lock_killable(&task->signal->cred_guard_mutex);
2711 return -EACCES; 2712 if (result)
2713 return result;
2714
2715 if (!ptrace_may_access(task, PTRACE_MODE_READ)) {
2716 result = -EACCES;
2717 goto out_unlock;
2718 }
2712 2719
2713 if (whole && lock_task_sighand(task, &flags)) { 2720 if (whole && lock_task_sighand(task, &flags)) {
2714 struct task_struct *t = task; 2721 struct task_struct *t = task;
@@ -2719,7 +2726,7 @@ static int do_io_accounting(struct task_struct *task, char *buffer, int whole)
2719 2726
2720 unlock_task_sighand(task, &flags); 2727 unlock_task_sighand(task, &flags);
2721 } 2728 }
2722 return sprintf(buffer, 2729 result = sprintf(buffer,
2723 "rchar: %llu\n" 2730 "rchar: %llu\n"
2724 "wchar: %llu\n" 2731 "wchar: %llu\n"
2725 "syscr: %llu\n" 2732 "syscr: %llu\n"
@@ -2734,6 +2741,9 @@ static int do_io_accounting(struct task_struct *task, char *buffer, int whole)
2734 (unsigned long long)acct.read_bytes, 2741 (unsigned long long)acct.read_bytes,
2735 (unsigned long long)acct.write_bytes, 2742 (unsigned long long)acct.write_bytes,
2736 (unsigned long long)acct.cancelled_write_bytes); 2743 (unsigned long long)acct.cancelled_write_bytes);
2744out_unlock:
2745 mutex_unlock(&task->signal->cred_guard_mutex);
2746 return result;
2737} 2747}
2738 2748
2739static int proc_tid_io_accounting(struct task_struct *task, char *buffer) 2749static int proc_tid_io_accounting(struct task_struct *task, char *buffer)