From: Amerigo Wang <amwang@redhat.com> Date: Mon, 11 May 2009 06:22:03 -0400 Subject: [fs] proc: avoid info leaks to non-privileged processes Message-id: 20090511102253.2662.24493.sendpatchset@localhost.localdomain O-Subject: [RHEL5 PATCH] BZ499541: kernel: proc: avoid information leaks to non-privileged processes Bugzilla: 499541 RH-Acked-by: Rik van Riel <riel@redhat.com> RH-Acked-by: Dave Anderson <anderson@redhat.com> RH-Acked-by: Chuck Ebbert <cebbert@redhat.com> BZ499541: https://bugzilla.redhat.com/show_bug.cgi?id=499541 Description of problem: 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. Upstream commit: http://git.kernel.org/linus/f83ce3e6b02d5e48b3a43b001390e2b58820389d Test status: I backport that patch to rhel5, and test it on x86_64, it is very fine. A sample test result is this: diff --git a/fs/proc/array.c b/fs/proc/array.c index fbb1718..e0c38b0 100644 --- a/fs/proc/array.c +++ b/fs/proc/array.c @@ -77,6 +77,7 @@ #include <linux/rcupdate.h> #include <linux/delayacct.h> #include <linux/resource.h> +#include <linux/ptrace.h> #include <asm/uaccess.h> #include <asm/pgtable.h> @@ -331,6 +332,7 @@ static int do_task_stat(struct task_struct *task, char * buffer, int whole) int res; pid_t ppid, 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; @@ -342,11 +344,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); @@ -399,7 +404,7 @@ static int do_task_stat(struct task_struct *task, char * buffer, int whole) read_unlock(&tasklist_lock); mutex_unlock(&tty_mutex); - if (!whole || num_threads<2) { + if (permitted && (!whole || num_threads<2)) { wchan = 0; if (current->uid == task->uid || current->euid == task->uid || capable(CAP_SYS_NICE)) @@ -453,7 +458,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 5e1f772..55daa0a 100644 --- a/fs/proc/base.c +++ b/fs/proc/base.c @@ -562,7 +562,10 @@ static int proc_pid_wchan(struct task_struct *task, char *buffer) sym_name = kallsyms_lookup(wchan, &size, &offset, &modname, namebuf); if (sym_name) return sprintf(buffer, "%s", sym_name); - return sprintf(buffer, "%lu", wchan); + if (!ptrace_may_attach(task)) + return 0; + else + return sprintf(buffer, "%lu", wchan); } #endif /* CONFIG_KALLSYMS */