From patchwork Tue Jun 21 09:35:23 2011 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Subject: [hardy, CVE, 1/2] proc: avoid information leaks to non-privileged processes From: Andy Whitcroft X-Patchwork-Id: 101262 Message-Id: <1308648925-13204-2-git-send-email-apw@canonical.com> To: kernel-team@lists.ubuntu.com Date: Tue, 21 Jun 2011 10:35:23 +0100 From: Jake Edge By using the same test as is used for /proc/pid/maps and /proc/pid/smaps, only allow processes that can ptrace() a given process to see information that might be used to bypass address space layout randomization (ASLR). These include eip, esp, wchan, and start_stack in /proc/pid/stat as well as the non-symbolic output from /proc/pid/wchan. ASLR can be bypassed by sampling eip as shown by the proof-of-concept code at http://code.google.com/p/fuzzyaslr/ As part of a presentation (http://www.cr0.org/paper/to-jt-linux-alsr-leak.pdf) esp and wchan were also noted as possibly usable information leaks as well. The start_stack address also leaks potentially useful information. Cc: Stable Team Signed-off-by: Jake Edge Acked-by: Arjan van de Ven Acked-by: "Eric W. Biederman" Signed-off-by: Linus Torvalds (backported from commit f83ce3e6b02d5e48b3a43b001390e2b58820389d) CVE-2011-0726 BugLink: http://bugs.launchpad.net/bugs/799906 Signed-off-by: Andy Whitcroft --- fs/proc/array.c | 13 +++++++++---- fs/proc/base.c | 5 ++++- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/fs/proc/array.c b/fs/proc/array.c index eb97f28..87902dd 100644 --- a/fs/proc/array.c +++ b/fs/proc/array.c @@ -78,6 +78,7 @@ #include #include #include +#include #include #include @@ -396,6 +397,7 @@ static int do_task_stat(struct task_struct *task, char *buffer, int whole) int res; pid_t ppid = 0, pgid = -1, sid = -1; int num_threads = 0; + int permitted; struct mm_struct *mm; unsigned long long start_time; unsigned long cmin_flt = 0, cmaj_flt = 0; @@ -411,11 +413,14 @@ static int do_task_stat(struct task_struct *task, char *buffer, int whole) state = *get_task_state(task); vsize = eip = esp = 0; + permitted = ptrace_may_attach(task); mm = get_task_mm(task); if (mm) { vsize = task_vsize(mm); - eip = KSTK_EIP(task); - esp = KSTK_ESP(task); + if (permitted) { + eip = KSTK_EIP(task); + esp = KSTK_ESP(task); + } } get_task_comm(tcomm, task); @@ -471,7 +476,7 @@ static int do_task_stat(struct task_struct *task, char *buffer, int whole) } rcu_read_unlock(); - if (!whole || num_threads < 2) + if (permitted && (!whole || num_threads < 2)) wchan = get_wchan(task); if (!whole) { min_flt = task->min_flt; @@ -523,7 +528,7 @@ static int do_task_stat(struct task_struct *task, char *buffer, int whole) rsslim, mm ? mm->start_code : 0, mm ? mm->end_code : 0, - mm ? mm->start_stack : 0, + (permitted && mm) ? mm->start_stack : 0, esp, eip, /* The signal information here is obsolete. diff --git a/fs/proc/base.c b/fs/proc/base.c index a91dc82..338097a 100644 --- a/fs/proc/base.c +++ b/fs/proc/base.c @@ -291,7 +291,10 @@ static int proc_pid_wchan(struct task_struct *task, char *buffer) wchan = get_wchan(task); if (lookup_symbol_name(wchan, symname) < 0) - return sprintf(buffer, "%lu", wchan); + if (!ptrace_may_attach(task)) + return 0; + else + return sprintf(buffer, "%lu", wchan); else return sprintf(buffer, "%s", symname); }