aboutsummaryrefslogtreecommitdiffstats
path: root/fs
diff options
context:
space:
mode:
authorStephen Wilson <wilsons@start.ca>2011-03-13 15:49:23 -0400
committerAl Viro <viro@zeniv.linux.org.uk>2011-03-23 16:36:59 -0400
commit8b0db9db19858b08c46a84540acfd35f6e6487b8 (patch)
treef8cad66b43b21ac8cc58c6173b86aaa9ee3d4b5f /fs
parent18f661bcf898742212182d75f22f05b048cc04bb (diff)
proc: make check_mem_permission() return an mm_struct on success
This change allows us to take advantage of access_remote_vm(), which in turn eliminates a security issue with the mem_write() implementation. The previous implementation of mem_write() was insecure since the target task could exec a setuid-root binary between the permission check and the actual write. Holding a reference to the target mm_struct eliminates this vulnerability. Signed-off-by: Stephen Wilson <wilsons@start.ca> Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
Diffstat (limited to 'fs')
-rw-r--r--fs/proc/base.c58
1 files changed, 34 insertions, 24 deletions
diff --git a/fs/proc/base.c b/fs/proc/base.c
index 013f116b3223..e34c3c33b2de 100644
--- a/fs/proc/base.c
+++ b/fs/proc/base.c
@@ -191,14 +191,20 @@ static int proc_root_link(struct inode *inode, struct path *path)
191 return result; 191 return result;
192} 192}
193 193
194static int __check_mem_permission(struct task_struct *task) 194static struct mm_struct *__check_mem_permission(struct task_struct *task)
195{ 195{
196 struct mm_struct *mm;
197
198 mm = get_task_mm(task);
199 if (!mm)
200 return ERR_PTR(-EINVAL);
201
196 /* 202 /*
197 * A task can always look at itself, in case it chooses 203 * A task can always look at itself, in case it chooses
198 * to use system calls instead of load instructions. 204 * to use system calls instead of load instructions.
199 */ 205 */
200 if (task == current) 206 if (task == current)
201 return 0; 207 return mm;
202 208
203 /* 209 /*
204 * If current is actively ptrace'ing, and would also be 210 * If current is actively ptrace'ing, and would also be
@@ -210,20 +216,23 @@ static int __check_mem_permission(struct task_struct *task)
210 match = (tracehook_tracer_task(task) == current); 216 match = (tracehook_tracer_task(task) == current);
211 rcu_read_unlock(); 217 rcu_read_unlock();
212 if (match && ptrace_may_access(task, PTRACE_MODE_ATTACH)) 218 if (match && ptrace_may_access(task, PTRACE_MODE_ATTACH))
213 return 0; 219 return mm;
214 } 220 }
215 221
216 /* 222 /*
217 * Noone else is allowed. 223 * Noone else is allowed.
218 */ 224 */
219 return -EPERM; 225 mmput(mm);
226 return ERR_PTR(-EPERM);
220} 227}
221 228
222/* 229/*
223 * Return zero if current may access user memory in @task, -error if not. 230 * If current may access user memory in @task return a reference to the
231 * corresponding mm, otherwise ERR_PTR.
224 */ 232 */
225static int check_mem_permission(struct task_struct *task) 233static struct mm_struct *check_mem_permission(struct task_struct *task)
226{ 234{
235 struct mm_struct *mm;
227 int err; 236 int err;
228 237
229 /* 238 /*
@@ -232,12 +241,12 @@ static int check_mem_permission(struct task_struct *task)
232 */ 241 */
233 err = mutex_lock_killable(&task->signal->cred_guard_mutex); 242 err = mutex_lock_killable(&task->signal->cred_guard_mutex);
234 if (err) 243 if (err)
235 return err; 244 return ERR_PTR(err);
236 245
237 err = __check_mem_permission(task); 246 mm = __check_mem_permission(task);
238 mutex_unlock(&task->signal->cred_guard_mutex); 247 mutex_unlock(&task->signal->cred_guard_mutex);
239 248
240 return err; 249 return mm;
241} 250}
242 251
243struct mm_struct *mm_for_maps(struct task_struct *task) 252struct mm_struct *mm_for_maps(struct task_struct *task)
@@ -795,18 +804,14 @@ static ssize_t mem_read(struct file * file, char __user * buf,
795 if (!task) 804 if (!task)
796 goto out_no_task; 805 goto out_no_task;
797 806
798 if (check_mem_permission(task))
799 goto out;
800
801 ret = -ENOMEM; 807 ret = -ENOMEM;
802 page = (char *)__get_free_page(GFP_TEMPORARY); 808 page = (char *)__get_free_page(GFP_TEMPORARY);
803 if (!page) 809 if (!page)
804 goto out; 810 goto out;
805 811
806 ret = 0; 812 mm = check_mem_permission(task);
807 813 ret = PTR_ERR(mm);
808 mm = get_task_mm(task); 814 if (IS_ERR(mm))
809 if (!mm)
810 goto out_free; 815 goto out_free;
811 816
812 ret = -EIO; 817 ret = -EIO;
@@ -820,8 +825,8 @@ static ssize_t mem_read(struct file * file, char __user * buf,
820 int this_len, retval; 825 int this_len, retval;
821 826
822 this_len = (count > PAGE_SIZE) ? PAGE_SIZE : count; 827 this_len = (count > PAGE_SIZE) ? PAGE_SIZE : count;
823 retval = access_process_vm(task, src, page, this_len, 0); 828 retval = access_remote_vm(mm, src, page, this_len, 0);
824 if (!retval || check_mem_permission(task)) { 829 if (!retval) {
825 if (!ret) 830 if (!ret)
826 ret = -EIO; 831 ret = -EIO;
827 break; 832 break;
@@ -860,22 +865,25 @@ static ssize_t mem_write(struct file * file, const char __user *buf,
860 char *page; 865 char *page;
861 struct task_struct *task = get_proc_task(file->f_path.dentry->d_inode); 866 struct task_struct *task = get_proc_task(file->f_path.dentry->d_inode);
862 unsigned long dst = *ppos; 867 unsigned long dst = *ppos;
868 struct mm_struct *mm;
863 869
864 copied = -ESRCH; 870 copied = -ESRCH;
865 if (!task) 871 if (!task)
866 goto out_no_task; 872 goto out_no_task;
867 873
868 if (check_mem_permission(task)) 874 mm = check_mem_permission(task);
869 goto out; 875 copied = PTR_ERR(mm);
876 if (IS_ERR(mm))
877 goto out_task;
870 878
871 copied = -EIO; 879 copied = -EIO;
872 if (file->private_data != (void *)((long)current->self_exec_id)) 880 if (file->private_data != (void *)((long)current->self_exec_id))
873 goto out; 881 goto out_mm;
874 882
875 copied = -ENOMEM; 883 copied = -ENOMEM;
876 page = (char *)__get_free_page(GFP_TEMPORARY); 884 page = (char *)__get_free_page(GFP_TEMPORARY);
877 if (!page) 885 if (!page)
878 goto out; 886 goto out_mm;
879 887
880 copied = 0; 888 copied = 0;
881 while (count > 0) { 889 while (count > 0) {
@@ -886,7 +894,7 @@ static ssize_t mem_write(struct file * file, const char __user *buf,
886 copied = -EFAULT; 894 copied = -EFAULT;
887 break; 895 break;
888 } 896 }
889 retval = access_process_vm(task, dst, page, this_len, 1); 897 retval = access_remote_vm(mm, dst, page, this_len, 1);
890 if (!retval) { 898 if (!retval) {
891 if (!copied) 899 if (!copied)
892 copied = -EIO; 900 copied = -EIO;
@@ -899,7 +907,9 @@ static ssize_t mem_write(struct file * file, const char __user *buf,
899 } 907 }
900 *ppos = dst; 908 *ppos = dst;
901 free_page((unsigned long) page); 909 free_page((unsigned long) page);
902out: 910out_mm:
911 mmput(mm);
912out_task:
903 put_task_struct(task); 913 put_task_struct(task);
904out_no_task: 914out_no_task:
905 return copied; 915 return copied;