restrict reading from /proc/<pid>/maps to those who share ->mm or can ptrace pid

Contents of /proc/*/maps is sensitive and may become sensitive after
open() (e.g. if target originally shares our ->mm and later does exec
on suid-root binary).

Check at read() (actually, ->start() of iterator) time that mm_struct
we'd grabbed and locked is
- still the ->mm of target
- equal to reader's ->mm or the target is ptracable by reader.

Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
Acked-by: Rik van Riel <riel@redhat.com>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>

authored by Al Viro and committed by Linus Torvalds 831830b5 ac40532e

+27 -7
+20
fs/proc/base.c
··· 202 202 (task->state == TASK_STOPPED || task->state == TASK_TRACED) && \ 203 203 security_ptrace(current,task) == 0)) 204 204 205 + struct mm_struct *mm_for_maps(struct task_struct *task) 206 + { 207 + struct mm_struct *mm = get_task_mm(task); 208 + if (!mm) 209 + return NULL; 210 + down_read(&mm->mmap_sem); 211 + task_lock(task); 212 + if (task->mm != mm) 213 + goto out; 214 + if (task->mm != current->mm && __ptrace_may_attach(task) < 0) 215 + goto out; 216 + task_unlock(task); 217 + return mm; 218 + out: 219 + task_unlock(task); 220 + up_read(&mm->mmap_sem); 221 + mmput(mm); 222 + return NULL; 223 + } 224 + 205 225 static int proc_pid_cmdline(struct task_struct *task, char * buffer) 206 226 { 207 227 int res = 0;
+2
fs/proc/internal.h
··· 27 27 unsigned long largest_chunk; 28 28 }; 29 29 30 + extern struct mm_struct *mm_for_maps(struct task_struct *); 31 + 30 32 #ifdef CONFIG_MMU 31 33 #define VMALLOC_TOTAL (VMALLOC_END - VMALLOC_START) 32 34 extern void get_vmalloc_info(struct vmalloc_info *vmi);
+1 -2
fs/proc/task_mmu.c
··· 397 397 if (!priv->task) 398 398 return NULL; 399 399 400 - mm = get_task_mm(priv->task); 400 + mm = mm_for_maps(priv->task); 401 401 if (!mm) 402 402 return NULL; 403 403 404 404 priv->tail_vma = tail_vma = get_gate_vma(priv->task); 405 - down_read(&mm->mmap_sem); 406 405 407 406 /* Start with last addr hint */ 408 407 if (last_addr && (vma = find_vma(mm, last_addr))) {
+1 -3
fs/proc/task_nommu.c
··· 165 165 if (!priv->task) 166 166 return NULL; 167 167 168 - mm = get_task_mm(priv->task); 168 + mm = mm_for_maps(priv->task); 169 169 if (!mm) { 170 170 put_task_struct(priv->task); 171 171 priv->task = NULL; 172 172 return NULL; 173 173 } 174 - 175 - down_read(&mm->mmap_sem); 176 174 177 175 /* start from the Nth VMA */ 178 176 for (vml = mm->context.vmlist; vml; vml = vml->next)
+1
include/linux/ptrace.h
··· 97 97 extern void __ptrace_unlink(struct task_struct *child); 98 98 extern void ptrace_untrace(struct task_struct *child); 99 99 extern int ptrace_may_attach(struct task_struct *task); 100 + extern int __ptrace_may_attach(struct task_struct *task); 100 101 101 102 static inline void ptrace_link(struct task_struct *child, 102 103 struct task_struct *new_parent)
+2 -2
kernel/ptrace.c
··· 120 120 return ret; 121 121 } 122 122 123 - static int may_attach(struct task_struct *task) 123 + int __ptrace_may_attach(struct task_struct *task) 124 124 { 125 125 /* May we inspect the given task? 126 126 * This check is used both for attaching with ptrace ··· 154 154 { 155 155 int err; 156 156 task_lock(task); 157 - err = may_attach(task); 157 + err = __ptrace_may_attach(task); 158 158 task_unlock(task); 159 159 return !err; 160 160 }