diff options
author | Oleg Nesterov <oleg@redhat.com> | 2014-10-09 18:25:26 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2014-10-09 22:25:48 -0400 |
commit | 29a40ace841cba9b661711f042d1821cdc4ad47c (patch) | |
tree | b04f8e2de4a4fd8e19889c2adfaef02235c678ec /fs/proc | |
parent | 5381e169e78405bd54256860f151596f5a887617 (diff) |
fs/proc/task_mmu.c: shift mm_access() from m_start() to proc_maps_open()
A simple test-case from Kirill Shutemov
cat /proc/self/maps >/dev/null
chmod +x /proc/self/net/packet
exec /proc/self/net/packet
makes lockdep unhappy, cat/exec take seq_file->lock + cred_guard_mutex in
the opposite order.
It's a false positive and probably we should not allow "chmod +x" on proc
files. Still I think that we should avoid mm_access() and cred_guard_mutex
in sys_read() paths, security checking should happen at open time. Besides,
this doesn't even look right if the task changes its ->mm between m_stop()
and m_start().
Add the new "mm_struct *mm" member into struct proc_maps_private and change
proc_maps_open() to initialize it using proc_mem_open(). Change m_start() to
use priv->mm if atomic_inc_not_zero(mm_users) succeeds or return NULL (eof)
otherwise.
The only complication is that proc_maps_open() users should additionally do
mmdrop() in fop->release(), add the new proc_map_release() helper for that.
Note: this is the user-visible change, if the task execs after open("maps")
the new ->mm won't be visible via this file. I hope this is fine, and this
matches /proc/pid/mem bahaviour.
[akpm@linux-foundation.org: coding-style fixes]
Signed-off-by: Oleg Nesterov <oleg@redhat.com>
Reported-by: "Kirill A. Shutemov" <kirill@shutemov.name>
Acked-by: Kirill A. Shutemov <kirill.shutemov@linux.intel.com>
Acked-by: Cyrill Gorcunov <gorcunov@openvz.org>
Cc: "Eric W. Biederman" <ebiederm@xmission.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'fs/proc')
-rw-r--r-- | fs/proc/internal.h | 1 | ||||
-rw-r--r-- | fs/proc/task_mmu.c | 37 |
2 files changed, 29 insertions, 9 deletions
diff --git a/fs/proc/internal.h b/fs/proc/internal.h index 3c685563406f..d27182854a28 100644 --- a/fs/proc/internal.h +++ b/fs/proc/internal.h | |||
@@ -270,6 +270,7 @@ extern int proc_remount(struct super_block *, int *, char *); | |||
270 | struct proc_maps_private { | 270 | struct proc_maps_private { |
271 | struct pid *pid; | 271 | struct pid *pid; |
272 | struct task_struct *task; | 272 | struct task_struct *task; |
273 | struct mm_struct *mm; | ||
273 | #ifdef CONFIG_MMU | 274 | #ifdef CONFIG_MMU |
274 | struct vm_area_struct *tail_vma; | 275 | struct vm_area_struct *tail_vma; |
275 | #endif | 276 | #endif |
diff --git a/fs/proc/task_mmu.c b/fs/proc/task_mmu.c index 4d716a09d500..a1454dac7e0a 100644 --- a/fs/proc/task_mmu.c +++ b/fs/proc/task_mmu.c | |||
@@ -165,9 +165,9 @@ static void *m_start(struct seq_file *m, loff_t *pos) | |||
165 | if (!priv->task) | 165 | if (!priv->task) |
166 | return ERR_PTR(-ESRCH); | 166 | return ERR_PTR(-ESRCH); |
167 | 167 | ||
168 | mm = mm_access(priv->task, PTRACE_MODE_READ); | 168 | mm = priv->mm; |
169 | if (!mm || IS_ERR(mm)) | 169 | if (!mm || !atomic_inc_not_zero(&mm->mm_users)) |
170 | return mm; | 170 | return NULL; |
171 | down_read(&mm->mmap_sem); | 171 | down_read(&mm->mmap_sem); |
172 | 172 | ||
173 | tail_vma = get_gate_vma(mm); | 173 | tail_vma = get_gate_vma(mm); |
@@ -240,9 +240,28 @@ static int proc_maps_open(struct inode *inode, struct file *file, | |||
240 | return -ENOMEM; | 240 | return -ENOMEM; |
241 | 241 | ||
242 | priv->pid = proc_pid(inode); | 242 | priv->pid = proc_pid(inode); |
243 | priv->mm = proc_mem_open(inode, PTRACE_MODE_READ); | ||
244 | if (IS_ERR(priv->mm)) { | ||
245 | int err = PTR_ERR(priv->mm); | ||
246 | |||
247 | seq_release_private(inode, file); | ||
248 | return err; | ||
249 | } | ||
250 | |||
243 | return 0; | 251 | return 0; |
244 | } | 252 | } |
245 | 253 | ||
254 | static int proc_map_release(struct inode *inode, struct file *file) | ||
255 | { | ||
256 | struct seq_file *seq = file->private_data; | ||
257 | struct proc_maps_private *priv = seq->private; | ||
258 | |||
259 | if (priv->mm) | ||
260 | mmdrop(priv->mm); | ||
261 | |||
262 | return seq_release_private(inode, file); | ||
263 | } | ||
264 | |||
246 | static int do_maps_open(struct inode *inode, struct file *file, | 265 | static int do_maps_open(struct inode *inode, struct file *file, |
247 | const struct seq_operations *ops) | 266 | const struct seq_operations *ops) |
248 | { | 267 | { |
@@ -398,14 +417,14 @@ const struct file_operations proc_pid_maps_operations = { | |||
398 | .open = pid_maps_open, | 417 | .open = pid_maps_open, |
399 | .read = seq_read, | 418 | .read = seq_read, |
400 | .llseek = seq_lseek, | 419 | .llseek = seq_lseek, |
401 | .release = seq_release_private, | 420 | .release = proc_map_release, |
402 | }; | 421 | }; |
403 | 422 | ||
404 | const struct file_operations proc_tid_maps_operations = { | 423 | const struct file_operations proc_tid_maps_operations = { |
405 | .open = tid_maps_open, | 424 | .open = tid_maps_open, |
406 | .read = seq_read, | 425 | .read = seq_read, |
407 | .llseek = seq_lseek, | 426 | .llseek = seq_lseek, |
408 | .release = seq_release_private, | 427 | .release = proc_map_release, |
409 | }; | 428 | }; |
410 | 429 | ||
411 | /* | 430 | /* |
@@ -680,14 +699,14 @@ const struct file_operations proc_pid_smaps_operations = { | |||
680 | .open = pid_smaps_open, | 699 | .open = pid_smaps_open, |
681 | .read = seq_read, | 700 | .read = seq_read, |
682 | .llseek = seq_lseek, | 701 | .llseek = seq_lseek, |
683 | .release = seq_release_private, | 702 | .release = proc_map_release, |
684 | }; | 703 | }; |
685 | 704 | ||
686 | const struct file_operations proc_tid_smaps_operations = { | 705 | const struct file_operations proc_tid_smaps_operations = { |
687 | .open = tid_smaps_open, | 706 | .open = tid_smaps_open, |
688 | .read = seq_read, | 707 | .read = seq_read, |
689 | .llseek = seq_lseek, | 708 | .llseek = seq_lseek, |
690 | .release = seq_release_private, | 709 | .release = proc_map_release, |
691 | }; | 710 | }; |
692 | 711 | ||
693 | /* | 712 | /* |
@@ -1544,13 +1563,13 @@ const struct file_operations proc_pid_numa_maps_operations = { | |||
1544 | .open = pid_numa_maps_open, | 1563 | .open = pid_numa_maps_open, |
1545 | .read = seq_read, | 1564 | .read = seq_read, |
1546 | .llseek = seq_lseek, | 1565 | .llseek = seq_lseek, |
1547 | .release = seq_release_private, | 1566 | .release = proc_map_release, |
1548 | }; | 1567 | }; |
1549 | 1568 | ||
1550 | const struct file_operations proc_tid_numa_maps_operations = { | 1569 | const struct file_operations proc_tid_numa_maps_operations = { |
1551 | .open = tid_numa_maps_open, | 1570 | .open = tid_numa_maps_open, |
1552 | .read = seq_read, | 1571 | .read = seq_read, |
1553 | .llseek = seq_lseek, | 1572 | .llseek = seq_lseek, |
1554 | .release = seq_release_private, | 1573 | .release = proc_map_release, |
1555 | }; | 1574 | }; |
1556 | #endif /* CONFIG_NUMA */ | 1575 | #endif /* CONFIG_NUMA */ |