aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorChristopher Yeoh <cyeoh@au1.ibm.com>2012-02-01 20:04:09 -0500
committerLinus Torvalds <torvalds@linux-foundation.org>2012-02-02 15:55:17 -0500
commit8cdb878dcb359fd1137e9abdee9322f5e9bcfdf8 (patch)
tree146afc01f3c1d7cbc944328484d077032bc53bfd
parent24b36da33c64368775f4ef9386d44dce1d2bc8cf (diff)
Fix race in process_vm_rw_core
This fixes the race in process_vm_core found by Oleg (see http://article.gmane.org/gmane.linux.kernel/1235667/ for details). This has been updated since I last sent it as the creation of the new mm_access() function did almost exactly the same thing as parts of the previous version of this patch did. In order to use mm_access() even when /proc isn't enabled, we move it to kernel/fork.c where other related process mm access functions already are. Signed-off-by: Chris Yeoh <yeohc@au1.ibm.com> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
-rw-r--r--fs/proc/base.c20
-rw-r--r--include/linux/sched.h6
-rw-r--r--kernel/fork.c20
-rw-r--r--mm/process_vm_access.c23
4 files changed, 35 insertions, 34 deletions
diff --git a/fs/proc/base.c b/fs/proc/base.c
index d9512bd03e6c..d4548dd49b02 100644
--- a/fs/proc/base.c
+++ b/fs/proc/base.c
@@ -198,26 +198,6 @@ static int proc_root_link(struct dentry *dentry, struct path *path)
198 return result; 198 return result;
199} 199}
200 200
201static struct mm_struct *mm_access(struct task_struct *task, unsigned int mode)
202{
203 struct mm_struct *mm;
204 int err;
205
206 err = mutex_lock_killable(&task->signal->cred_guard_mutex);
207 if (err)
208 return ERR_PTR(err);
209
210 mm = get_task_mm(task);
211 if (mm && mm != current->mm &&
212 !ptrace_may_access(task, mode)) {
213 mmput(mm);
214 mm = ERR_PTR(-EACCES);
215 }
216 mutex_unlock(&task->signal->cred_guard_mutex);
217
218 return mm;
219}
220
221struct mm_struct *mm_for_maps(struct task_struct *task) 201struct mm_struct *mm_for_maps(struct task_struct *task)
222{ 202{
223 return mm_access(task, PTRACE_MODE_READ); 203 return mm_access(task, PTRACE_MODE_READ);
diff --git a/include/linux/sched.h b/include/linux/sched.h
index 2234985a5e65..7d379a6bfd88 100644
--- a/include/linux/sched.h
+++ b/include/linux/sched.h
@@ -2259,6 +2259,12 @@ static inline void mmdrop(struct mm_struct * mm)
2259extern void mmput(struct mm_struct *); 2259extern void mmput(struct mm_struct *);
2260/* Grab a reference to a task's mm, if it is not already going away */ 2260/* Grab a reference to a task's mm, if it is not already going away */
2261extern struct mm_struct *get_task_mm(struct task_struct *task); 2261extern struct mm_struct *get_task_mm(struct task_struct *task);
2262/*
2263 * Grab a reference to a task's mm, if it is not already going away
2264 * and ptrace_may_access with the mode parameter passed to it
2265 * succeeds.
2266 */
2267extern struct mm_struct *mm_access(struct task_struct *task, unsigned int mode);
2262/* Remove the current tasks stale references to the old mm_struct */ 2268/* Remove the current tasks stale references to the old mm_struct */
2263extern void mm_release(struct task_struct *, struct mm_struct *); 2269extern void mm_release(struct task_struct *, struct mm_struct *);
2264/* Allocate a new mm structure and copy contents from tsk->mm */ 2270/* Allocate a new mm structure and copy contents from tsk->mm */
diff --git a/kernel/fork.c b/kernel/fork.c
index 051f090d40c1..1b2ef3c23ae4 100644
--- a/kernel/fork.c
+++ b/kernel/fork.c
@@ -647,6 +647,26 @@ struct mm_struct *get_task_mm(struct task_struct *task)
647} 647}
648EXPORT_SYMBOL_GPL(get_task_mm); 648EXPORT_SYMBOL_GPL(get_task_mm);
649 649
650struct mm_struct *mm_access(struct task_struct *task, unsigned int mode)
651{
652 struct mm_struct *mm;
653 int err;
654
655 err = mutex_lock_killable(&task->signal->cred_guard_mutex);
656 if (err)
657 return ERR_PTR(err);
658
659 mm = get_task_mm(task);
660 if (mm && mm != current->mm &&
661 !ptrace_may_access(task, mode)) {
662 mmput(mm);
663 mm = ERR_PTR(-EACCES);
664 }
665 mutex_unlock(&task->signal->cred_guard_mutex);
666
667 return mm;
668}
669
650/* Please note the differences between mmput and mm_release. 670/* Please note the differences between mmput and mm_release.
651 * mmput is called whenever we stop holding onto a mm_struct, 671 * mmput is called whenever we stop holding onto a mm_struct,
652 * error success whatever. 672 * error success whatever.
diff --git a/mm/process_vm_access.c b/mm/process_vm_access.c
index e920aa3ce104..c20ff48994c2 100644
--- a/mm/process_vm_access.c
+++ b/mm/process_vm_access.c
@@ -298,23 +298,18 @@ static ssize_t process_vm_rw_core(pid_t pid, const struct iovec *lvec,
298 goto free_proc_pages; 298 goto free_proc_pages;
299 } 299 }
300 300
301 task_lock(task); 301 mm = mm_access(task, PTRACE_MODE_ATTACH);
302 if (__ptrace_may_access(task, PTRACE_MODE_ATTACH)) { 302 if (!mm || IS_ERR(mm)) {
303 task_unlock(task); 303 rc = IS_ERR(mm) ? PTR_ERR(mm) : -ESRCH;
304 rc = -EPERM; 304 /*
305 goto put_task_struct; 305 * Explicitly map EACCES to EPERM as EPERM is a more a
306 } 306 * appropriate error code for process_vw_readv/writev
307 mm = task->mm; 307 */
308 308 if (rc == -EACCES)
309 if (!mm || (task->flags & PF_KTHREAD)) { 309 rc = -EPERM;
310 task_unlock(task);
311 rc = -EINVAL;
312 goto put_task_struct; 310 goto put_task_struct;
313 } 311 }
314 312
315 atomic_inc(&mm->mm_users);
316 task_unlock(task);
317
318 for (i = 0; i < riovcnt && iov_l_curr_idx < liovcnt; i++) { 313 for (i = 0; i < riovcnt && iov_l_curr_idx < liovcnt; i++) {
319 rc = process_vm_rw_single_vec( 314 rc = process_vm_rw_single_vec(
320 (unsigned long)rvec[i].iov_base, rvec[i].iov_len, 315 (unsigned long)rvec[i].iov_base, rvec[i].iov_len,