aboutsummaryrefslogtreecommitdiffstats
path: root/fs/proc
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2012-01-17 18:21:19 -0500
committerHerton Ronaldo Krzesinski <herton.krzesinski@canonical.com>2012-02-13 15:14:57 -0500
commitb85dd915a1d64afac55a99a47baf45f095799def (patch)
treeca09857f4778b1ea5430e59f90bf899f17fcec2c /fs/proc
parent581389224a23eec49cd3e899bcce91eedf378d24 (diff)
proc: clean up and fix /proc/<pid>/mem handling
BugLink: http://bugs.launchpad.net/bugs/922799 commit e268337dfe26dfc7efd422a804dbb27977a3cccc upstream. Jüri Aedla reported that the /proc/<pid>/mem handling really isn't very robust, and it also doesn't match the permission checking of any of the other related files. This changes it to do the permission checks at open time, and instead of tracking the process, it tracks the VM at the time of the open. That simplifies the code a lot, but does mean that if you hold the file descriptor open over an execve(), you'll continue to read from the _old_ VM. That is different from our previous behavior, but much simpler. If somebody actually finds a load where this matters, we'll need to revert this commit. I suspect that nobody will ever notice - because the process mapping addresses will also have changed as part of the execve. So you cannot actually usefully access the fd across a VM change simply because all the offsets for IO would have changed too. Reported-by: Jüri Aedla <asd@ut.ee> Cc: Al Viro <viro@zeniv.linux.org.uk> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'fs/proc')
-rw-r--r--fs/proc/base.c145
1 files changed, 39 insertions, 106 deletions
diff --git a/fs/proc/base.c b/fs/proc/base.c
index 22511548d90..a70150ae505 100644
--- a/fs/proc/base.c
+++ b/fs/proc/base.c
@@ -195,65 +195,7 @@ static int proc_root_link(struct inode *inode, struct path *path)
195 return result; 195 return result;
196} 196}
197 197
198static struct mm_struct *__check_mem_permission(struct task_struct *task) 198static struct mm_struct *mm_access(struct task_struct *task, unsigned int mode)
199{
200 struct mm_struct *mm;
201
202 mm = get_task_mm(task);
203 if (!mm)
204 return ERR_PTR(-EINVAL);
205
206 /*
207 * A task can always look at itself, in case it chooses
208 * to use system calls instead of load instructions.
209 */
210 if (task == current)
211 return mm;
212
213 /*
214 * If current is actively ptrace'ing, and would also be
215 * permitted to freshly attach with ptrace now, permit it.
216 */
217 if (task_is_stopped_or_traced(task)) {
218 int match;
219 rcu_read_lock();
220 match = (tracehook_tracer_task(task) == current);
221 rcu_read_unlock();
222 if (match && ptrace_may_access(task, PTRACE_MODE_ATTACH))
223 return mm;
224 }
225
226 /*
227 * No one else is allowed.
228 */
229 mmput(mm);
230 return ERR_PTR(-EPERM);
231}
232
233/*
234 * If current may access user memory in @task return a reference to the
235 * corresponding mm, otherwise ERR_PTR.
236 */
237static struct mm_struct *check_mem_permission(struct task_struct *task)
238{
239 struct mm_struct *mm;
240 int err;
241
242 /*
243 * Avoid racing if task exec's as we might get a new mm but validate
244 * against old credentials.
245 */
246 err = mutex_lock_killable(&task->signal->cred_guard_mutex);
247 if (err)
248 return ERR_PTR(err);
249
250 mm = __check_mem_permission(task);
251 mutex_unlock(&task->signal->cred_guard_mutex);
252
253 return mm;
254}
255
256struct mm_struct *mm_for_maps(struct task_struct *task)
257{ 199{
258 struct mm_struct *mm; 200 struct mm_struct *mm;
259 int err; 201 int err;
@@ -264,7 +206,7 @@ struct mm_struct *mm_for_maps(struct task_struct *task)
264 206
265 mm = get_task_mm(task); 207 mm = get_task_mm(task);
266 if (mm && mm != current->mm && 208 if (mm && mm != current->mm &&
267 !ptrace_may_access(task, PTRACE_MODE_READ)) { 209 !ptrace_may_access(task, mode)) {
268 mmput(mm); 210 mmput(mm);
269 mm = ERR_PTR(-EACCES); 211 mm = ERR_PTR(-EACCES);
270 } 212 }
@@ -273,6 +215,11 @@ struct mm_struct *mm_for_maps(struct task_struct *task)
273 return mm; 215 return mm;
274} 216}
275 217
218struct mm_struct *mm_for_maps(struct task_struct *task)
219{
220 return mm_access(task, PTRACE_MODE_READ);
221}
222
276static int proc_pid_cmdline(struct task_struct *task, char * buffer) 223static int proc_pid_cmdline(struct task_struct *task, char * buffer)
277{ 224{
278 int res = 0; 225 int res = 0;
@@ -841,38 +788,39 @@ static const struct file_operations proc_single_file_operations = {
841 788
842static int mem_open(struct inode* inode, struct file* file) 789static int mem_open(struct inode* inode, struct file* file)
843{ 790{
844 file->private_data = (void*)((long)current->self_exec_id); 791 struct task_struct *task = get_proc_task(file->f_path.dentry->d_inode);
792 struct mm_struct *mm;
793
794 if (!task)
795 return -ESRCH;
796
797 mm = mm_access(task, PTRACE_MODE_ATTACH);
798 put_task_struct(task);
799
800 if (IS_ERR(mm))
801 return PTR_ERR(mm);
802
845 /* OK to pass negative loff_t, we can catch out-of-range */ 803 /* OK to pass negative loff_t, we can catch out-of-range */
846 file->f_mode |= FMODE_UNSIGNED_OFFSET; 804 file->f_mode |= FMODE_UNSIGNED_OFFSET;
805 file->private_data = mm;
806
847 return 0; 807 return 0;
848} 808}
849 809
850static ssize_t mem_read(struct file * file, char __user * buf, 810static ssize_t mem_read(struct file * file, char __user * buf,
851 size_t count, loff_t *ppos) 811 size_t count, loff_t *ppos)
852{ 812{
853 struct task_struct *task = get_proc_task(file->f_path.dentry->d_inode); 813 int ret;
854 char *page; 814 char *page;
855 unsigned long src = *ppos; 815 unsigned long src = *ppos;
856 int ret = -ESRCH; 816 struct mm_struct *mm = file->private_data;
857 struct mm_struct *mm;
858 817
859 if (!task) 818 if (!mm)
860 goto out_no_task; 819 return 0;
861 820
862 ret = -ENOMEM;
863 page = (char *)__get_free_page(GFP_TEMPORARY); 821 page = (char *)__get_free_page(GFP_TEMPORARY);
864 if (!page) 822 if (!page)
865 goto out; 823 return -ENOMEM;
866
867 mm = check_mem_permission(task);
868 ret = PTR_ERR(mm);
869 if (IS_ERR(mm))
870 goto out_free;
871
872 ret = -EIO;
873
874 if (file->private_data != (void*)((long)current->self_exec_id))
875 goto out_put;
876 824
877 ret = 0; 825 ret = 0;
878 826
@@ -899,13 +847,7 @@ static ssize_t mem_read(struct file * file, char __user * buf,
899 } 847 }
900 *ppos = src; 848 *ppos = src;
901 849
902out_put:
903 mmput(mm);
904out_free:
905 free_page((unsigned long) page); 850 free_page((unsigned long) page);
906out:
907 put_task_struct(task);
908out_no_task:
909 return ret; 851 return ret;
910} 852}
911 853
@@ -914,27 +856,15 @@ static ssize_t mem_write(struct file * file, const char __user *buf,
914{ 856{
915 int copied; 857 int copied;
916 char *page; 858 char *page;
917 struct task_struct *task = get_proc_task(file->f_path.dentry->d_inode);
918 unsigned long dst = *ppos; 859 unsigned long dst = *ppos;
919 struct mm_struct *mm; 860 struct mm_struct *mm = file->private_data;
920 861
921 copied = -ESRCH; 862 if (!mm)
922 if (!task) 863 return 0;
923 goto out_no_task;
924 864
925 copied = -ENOMEM;
926 page = (char *)__get_free_page(GFP_TEMPORARY); 865 page = (char *)__get_free_page(GFP_TEMPORARY);
927 if (!page) 866 if (!page)
928 goto out_task; 867 return -ENOMEM;
929
930 mm = check_mem_permission(task);
931 copied = PTR_ERR(mm);
932 if (IS_ERR(mm))
933 goto out_free;
934
935 copied = -EIO;
936 if (file->private_data != (void *)((long)current->self_exec_id))
937 goto out_mm;
938 868
939 copied = 0; 869 copied = 0;
940 while (count > 0) { 870 while (count > 0) {
@@ -958,13 +888,7 @@ static ssize_t mem_write(struct file * file, const char __user *buf,
958 } 888 }
959 *ppos = dst; 889 *ppos = dst;
960 890
961out_mm:
962 mmput(mm);
963out_free:
964 free_page((unsigned long) page); 891 free_page((unsigned long) page);
965out_task:
966 put_task_struct(task);
967out_no_task:
968 return copied; 892 return copied;
969} 893}
970 894
@@ -984,11 +908,20 @@ loff_t mem_lseek(struct file *file, loff_t offset, int orig)
984 return file->f_pos; 908 return file->f_pos;
985} 909}
986 910
911static int mem_release(struct inode *inode, struct file *file)
912{
913 struct mm_struct *mm = file->private_data;
914
915 mmput(mm);
916 return 0;
917}
918
987static const struct file_operations proc_mem_operations = { 919static const struct file_operations proc_mem_operations = {
988 .llseek = mem_lseek, 920 .llseek = mem_lseek,
989 .read = mem_read, 921 .read = mem_read,
990 .write = mem_write, 922 .write = mem_write,
991 .open = mem_open, 923 .open = mem_open,
924 .release = mem_release,
992}; 925};
993 926
994static ssize_t environ_read(struct file *file, char __user *buf, 927static ssize_t environ_read(struct file *file, char __user *buf,