Commit 5096add84b9e96e2e0a9c72675c442fe5433388a

Authored by Kees Cook
Committed by Linus Torvalds
1 parent 4a1ccb5b1e

proc: maps protection

The /proc/pid/ "maps", "smaps", and "numa_maps" files contain sensitive
information about the memory location and usage of processes.  Issues:

- maps should not be world-readable, especially if programs expect any
  kind of ASLR protection from local attackers.
- maps cannot just be 0400 because "-D_FORTIFY_SOURCE=2 -O2" makes glibc
  check the maps when %n is in a *printf call, and a setuid(getuid())
  process wouldn't be able to read its own maps file.  (For reference
  see http://lkml.org/lkml/2006/1/22/150)
- a system-wide toggle is needed to allow prior behavior in the case of
  non-root applications that depend on access to the maps contents.

This change implements a check using "ptrace_may_attach" before allowing
access to read the maps contents.  To control this protection, the new knob
/proc/sys/kernel/maps_protect has been added, with corresponding updates to
the procfs documentation.

[akpm@linux-foundation.org: build fixes]
[akpm@linux-foundation.org: New sysctl numbers are old hat]
Signed-off-by: Kees Cook <kees@outflux.net>
Cc: Arjan van de Ven <arjan@infradead.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>

Showing 7 changed files with 48 additions and 2 deletions Side-by-side Diff

... ... @@ -661,7 +661,7 @@
661 661 E: kees@outflux.net
662 662 W: http://outflux.net/
663 663 P: 1024D/17063E6D 9FA3 C49C 23C9 D1BC 2E30 1975 1FFF 4BA9 1706 3E6D
664   -D: Minor updates to SCSI code for the Communications type
  664 +D: Minor updates to SCSI types, added /proc/pid/maps protection
665 665 S: (ask for current address)
666 666 S: USA
667 667  
Documentation/filesystems/proc.txt
... ... @@ -1138,6 +1138,13 @@
1138 1138 Because the NMI watchdog shares registers with oprofile, by disabling the NMI
1139 1139 watchdog, oprofile may have more registers to utilize.
1140 1140  
  1141 +maps_protect
  1142 +------------
  1143 +
  1144 +Enables/Disables the protection of the per-process proc entries "maps" and
  1145 +"smaps". When enabled, the contents of these files are visible only to
  1146 +readers that are allowed to ptrace() the given process.
  1147 +
1141 1148  
1142 1149 2.4 /proc/sys/vm - The virtual memory subsystem
1143 1150 -----------------------------------------------
... ... @@ -64,6 +64,7 @@
64 64 #include <linux/smp_lock.h>
65 65 #include <linux/rcupdate.h>
66 66 #include <linux/kallsyms.h>
  67 +#include <linux/module.h>
67 68 #include <linux/mount.h>
68 69 #include <linux/security.h>
69 70 #include <linux/ptrace.h>
... ... @@ -122,6 +123,9 @@
122 123 NOD(NAME, (S_IFREG|(MODE)), \
123 124 NULL, &proc_info_file_operations, \
124 125 { .proc_read = &proc_##OTYPE } )
  126 +
  127 +int maps_protect;
  128 +EXPORT_SYMBOL(maps_protect);
125 129  
126 130 static struct fs_struct *get_fs_struct(struct task_struct *task)
127 131 {
... ... @@ -37,6 +37,8 @@
37 37 extern int nommu_vma_show(struct seq_file *, struct vm_area_struct *);
38 38 #endif
39 39  
  40 +extern int maps_protect;
  41 +
40 42 extern void create_seq_entry(char *name, mode_t mode, const struct file_operations *f);
41 43 extern int proc_exe_link(struct inode *, struct dentry **, struct vfsmount **);
42 44 extern int proc_tid_stat(struct task_struct *, char *);
... ... @@ -3,6 +3,7 @@
3 3 #include <linux/mount.h>
4 4 #include <linux/seq_file.h>
5 5 #include <linux/highmem.h>
  6 +#include <linux/ptrace.h>
6 7 #include <linux/pagemap.h>
7 8 #include <linux/mempolicy.h>
8 9  
... ... @@ -142,6 +143,9 @@
142 143 dev_t dev = 0;
143 144 int len;
144 145  
  146 + if (maps_protect && !ptrace_may_attach(task))
  147 + return -EACCES;
  148 +
145 149 if (file) {
146 150 struct inode *inode = vma->vm_file->f_path.dentry->d_inode;
147 151 dev = inode->i_sb->s_dev;
148 152  
... ... @@ -512,11 +516,22 @@
512 516 #ifdef CONFIG_NUMA
513 517 extern int show_numa_map(struct seq_file *m, void *v);
514 518  
  519 +static int show_numa_map_checked(struct seq_file *m, void *v)
  520 +{
  521 + struct proc_maps_private *priv = m->private;
  522 + struct task_struct *task = priv->task;
  523 +
  524 + if (maps_protect && !ptrace_may_attach(task))
  525 + return -EACCES;
  526 +
  527 + return show_numa_map(m, v);
  528 +}
  529 +
515 530 static struct seq_operations proc_pid_numa_maps_op = {
516 531 .start = m_start,
517 532 .next = m_next,
518 533 .stop = m_stop,
519   - .show = show_numa_map
  534 + .show = show_numa_map_checked
520 535 };
521 536  
522 537 static int numa_maps_open(struct inode *inode, struct file *file)
fs/proc/task_nommu.c
... ... @@ -2,6 +2,7 @@
2 2 #include <linux/mm.h>
3 3 #include <linux/file.h>
4 4 #include <linux/mount.h>
  5 +#include <linux/ptrace.h>
5 6 #include <linux/seq_file.h>
6 7 #include "internal.h"
7 8  
... ... @@ -143,6 +144,12 @@
143 144 static int show_map(struct seq_file *m, void *_vml)
144 145 {
145 146 struct vm_list_struct *vml = _vml;
  147 + struct proc_maps_private *priv = m->private;
  148 + struct task_struct *task = priv->task;
  149 +
  150 + if (maps_protect && !ptrace_may_attach(task))
  151 + return -EACCES;
  152 +
146 153 return nommu_vma_show(m, vml->vma);
147 154 }
148 155  
... ... @@ -76,6 +76,7 @@
76 76 extern int sysctl_drop_caches;
77 77 extern int percpu_pagelist_fraction;
78 78 extern int compat_log;
  79 +extern int maps_protect;
79 80  
80 81 /* this is needed for the proc_dointvec_minmax for [fs_]overflow UID and GID */
81 82 static int maxolduid = 65535;
... ... @@ -601,6 +602,16 @@
601 602 .maxlen = sizeof(int),
602 603 .mode = 0644,
603 604 .proc_handler = &proc_dointvec,
  605 + },
  606 +#endif
  607 +#ifdef CONFIG_PROC_FS
  608 + {
  609 + .ctl_name = CTL_UNNUMBERED,
  610 + .procname = "maps_protect",
  611 + .data = &maps_protect,
  612 + .maxlen = sizeof(int),
  613 + .mode = 0644,
  614 + .proc_handler = &proc_dointvec,
604 615 },
605 616 #endif
606 617