diff options
Diffstat (limited to 'fs/proc/base.c')
-rw-r--r-- | fs/proc/base.c | 145 |
1 files changed, 39 insertions, 106 deletions
diff --git a/fs/proc/base.c b/fs/proc/base.c index 5485a5388ecb..662ddf2ec4f1 100644 --- a/fs/proc/base.c +++ b/fs/proc/base.c | |||
@@ -198,65 +198,7 @@ static int proc_root_link(struct dentry *dentry, struct path *path) | |||
198 | return result; | 198 | return result; |
199 | } | 199 | } |
200 | 200 | ||
201 | static struct mm_struct *__check_mem_permission(struct task_struct *task) | 201 | static struct mm_struct *mm_access(struct task_struct *task, unsigned int mode) |
202 | { | ||
203 | struct mm_struct *mm; | ||
204 | |||
205 | mm = get_task_mm(task); | ||
206 | if (!mm) | ||
207 | return ERR_PTR(-EINVAL); | ||
208 | |||
209 | /* | ||
210 | * A task can always look at itself, in case it chooses | ||
211 | * to use system calls instead of load instructions. | ||
212 | */ | ||
213 | if (task == current) | ||
214 | return mm; | ||
215 | |||
216 | /* | ||
217 | * If current is actively ptrace'ing, and would also be | ||
218 | * permitted to freshly attach with ptrace now, permit it. | ||
219 | */ | ||
220 | if (task_is_stopped_or_traced(task)) { | ||
221 | int match; | ||
222 | rcu_read_lock(); | ||
223 | match = (ptrace_parent(task) == current); | ||
224 | rcu_read_unlock(); | ||
225 | if (match && ptrace_may_access(task, PTRACE_MODE_ATTACH)) | ||
226 | return mm; | ||
227 | } | ||
228 | |||
229 | /* | ||
230 | * No one else is allowed. | ||
231 | */ | ||
232 | mmput(mm); | ||
233 | return ERR_PTR(-EPERM); | ||
234 | } | ||
235 | |||
236 | /* | ||
237 | * If current may access user memory in @task return a reference to the | ||
238 | * corresponding mm, otherwise ERR_PTR. | ||
239 | */ | ||
240 | static struct mm_struct *check_mem_permission(struct task_struct *task) | ||
241 | { | ||
242 | struct mm_struct *mm; | ||
243 | int err; | ||
244 | |||
245 | /* | ||
246 | * Avoid racing if task exec's as we might get a new mm but validate | ||
247 | * against old credentials. | ||
248 | */ | ||
249 | err = mutex_lock_killable(&task->signal->cred_guard_mutex); | ||
250 | if (err) | ||
251 | return ERR_PTR(err); | ||
252 | |||
253 | mm = __check_mem_permission(task); | ||
254 | mutex_unlock(&task->signal->cred_guard_mutex); | ||
255 | |||
256 | return mm; | ||
257 | } | ||
258 | |||
259 | struct mm_struct *mm_for_maps(struct task_struct *task) | ||
260 | { | 202 | { |
261 | struct mm_struct *mm; | 203 | struct mm_struct *mm; |
262 | int err; | 204 | int err; |
@@ -267,7 +209,7 @@ struct mm_struct *mm_for_maps(struct task_struct *task) | |||
267 | 209 | ||
268 | mm = get_task_mm(task); | 210 | mm = get_task_mm(task); |
269 | if (mm && mm != current->mm && | 211 | if (mm && mm != current->mm && |
270 | !ptrace_may_access(task, PTRACE_MODE_READ)) { | 212 | !ptrace_may_access(task, mode)) { |
271 | mmput(mm); | 213 | mmput(mm); |
272 | mm = ERR_PTR(-EACCES); | 214 | mm = ERR_PTR(-EACCES); |
273 | } | 215 | } |
@@ -276,6 +218,11 @@ struct mm_struct *mm_for_maps(struct task_struct *task) | |||
276 | return mm; | 218 | return mm; |
277 | } | 219 | } |
278 | 220 | ||
221 | struct mm_struct *mm_for_maps(struct task_struct *task) | ||
222 | { | ||
223 | return mm_access(task, PTRACE_MODE_READ); | ||
224 | } | ||
225 | |||
279 | static int proc_pid_cmdline(struct task_struct *task, char * buffer) | 226 | static int proc_pid_cmdline(struct task_struct *task, char * buffer) |
280 | { | 227 | { |
281 | int res = 0; | 228 | int res = 0; |
@@ -752,38 +699,39 @@ static const struct file_operations proc_single_file_operations = { | |||
752 | 699 | ||
753 | static int mem_open(struct inode* inode, struct file* file) | 700 | static int mem_open(struct inode* inode, struct file* file) |
754 | { | 701 | { |
755 | file->private_data = (void*)((long)current->self_exec_id); | 702 | struct task_struct *task = get_proc_task(file->f_path.dentry->d_inode); |
703 | struct mm_struct *mm; | ||
704 | |||
705 | if (!task) | ||
706 | return -ESRCH; | ||
707 | |||
708 | mm = mm_access(task, PTRACE_MODE_ATTACH); | ||
709 | put_task_struct(task); | ||
710 | |||
711 | if (IS_ERR(mm)) | ||
712 | return PTR_ERR(mm); | ||
713 | |||
756 | /* OK to pass negative loff_t, we can catch out-of-range */ | 714 | /* OK to pass negative loff_t, we can catch out-of-range */ |
757 | file->f_mode |= FMODE_UNSIGNED_OFFSET; | 715 | file->f_mode |= FMODE_UNSIGNED_OFFSET; |
716 | file->private_data = mm; | ||
717 | |||
758 | return 0; | 718 | return 0; |
759 | } | 719 | } |
760 | 720 | ||
761 | static ssize_t mem_read(struct file * file, char __user * buf, | 721 | static ssize_t mem_read(struct file * file, char __user * buf, |
762 | size_t count, loff_t *ppos) | 722 | size_t count, loff_t *ppos) |
763 | { | 723 | { |
764 | struct task_struct *task = get_proc_task(file->f_path.dentry->d_inode); | 724 | int ret; |
765 | char *page; | 725 | char *page; |
766 | unsigned long src = *ppos; | 726 | unsigned long src = *ppos; |
767 | int ret = -ESRCH; | 727 | struct mm_struct *mm = file->private_data; |
768 | struct mm_struct *mm; | ||
769 | 728 | ||
770 | if (!task) | 729 | if (!mm) |
771 | goto out_no_task; | 730 | return 0; |
772 | 731 | ||
773 | ret = -ENOMEM; | ||
774 | page = (char *)__get_free_page(GFP_TEMPORARY); | 732 | page = (char *)__get_free_page(GFP_TEMPORARY); |
775 | if (!page) | 733 | if (!page) |
776 | goto out; | 734 | return -ENOMEM; |
777 | |||
778 | mm = check_mem_permission(task); | ||
779 | ret = PTR_ERR(mm); | ||
780 | if (IS_ERR(mm)) | ||
781 | goto out_free; | ||
782 | |||
783 | ret = -EIO; | ||
784 | |||
785 | if (file->private_data != (void*)((long)current->self_exec_id)) | ||
786 | goto out_put; | ||
787 | 735 | ||
788 | ret = 0; | 736 | ret = 0; |
789 | 737 | ||
@@ -810,13 +758,7 @@ static ssize_t mem_read(struct file * file, char __user * buf, | |||
810 | } | 758 | } |
811 | *ppos = src; | 759 | *ppos = src; |
812 | 760 | ||
813 | out_put: | ||
814 | mmput(mm); | ||
815 | out_free: | ||
816 | free_page((unsigned long) page); | 761 | free_page((unsigned long) page); |
817 | out: | ||
818 | put_task_struct(task); | ||
819 | out_no_task: | ||
820 | return ret; | 762 | return ret; |
821 | } | 763 | } |
822 | 764 | ||
@@ -825,27 +767,15 @@ static ssize_t mem_write(struct file * file, const char __user *buf, | |||
825 | { | 767 | { |
826 | int copied; | 768 | int copied; |
827 | char *page; | 769 | char *page; |
828 | struct task_struct *task = get_proc_task(file->f_path.dentry->d_inode); | ||
829 | unsigned long dst = *ppos; | 770 | unsigned long dst = *ppos; |
830 | struct mm_struct *mm; | 771 | struct mm_struct *mm = file->private_data; |
831 | 772 | ||
832 | copied = -ESRCH; | 773 | if (!mm) |
833 | if (!task) | 774 | return 0; |
834 | goto out_no_task; | ||
835 | 775 | ||
836 | copied = -ENOMEM; | ||
837 | page = (char *)__get_free_page(GFP_TEMPORARY); | 776 | page = (char *)__get_free_page(GFP_TEMPORARY); |
838 | if (!page) | 777 | if (!page) |
839 | goto out_task; | 778 | return -ENOMEM; |
840 | |||
841 | mm = check_mem_permission(task); | ||
842 | copied = PTR_ERR(mm); | ||
843 | if (IS_ERR(mm)) | ||
844 | goto out_free; | ||
845 | |||
846 | copied = -EIO; | ||
847 | if (file->private_data != (void *)((long)current->self_exec_id)) | ||
848 | goto out_mm; | ||
849 | 779 | ||
850 | copied = 0; | 780 | copied = 0; |
851 | while (count > 0) { | 781 | while (count > 0) { |
@@ -869,13 +799,7 @@ static ssize_t mem_write(struct file * file, const char __user *buf, | |||
869 | } | 799 | } |
870 | *ppos = dst; | 800 | *ppos = dst; |
871 | 801 | ||
872 | out_mm: | ||
873 | mmput(mm); | ||
874 | out_free: | ||
875 | free_page((unsigned long) page); | 802 | free_page((unsigned long) page); |
876 | out_task: | ||
877 | put_task_struct(task); | ||
878 | out_no_task: | ||
879 | return copied; | 803 | return copied; |
880 | } | 804 | } |
881 | 805 | ||
@@ -895,11 +819,20 @@ loff_t mem_lseek(struct file *file, loff_t offset, int orig) | |||
895 | return file->f_pos; | 819 | return file->f_pos; |
896 | } | 820 | } |
897 | 821 | ||
822 | static int mem_release(struct inode *inode, struct file *file) | ||
823 | { | ||
824 | struct mm_struct *mm = file->private_data; | ||
825 | |||
826 | mmput(mm); | ||
827 | return 0; | ||
828 | } | ||
829 | |||
898 | static const struct file_operations proc_mem_operations = { | 830 | static const struct file_operations proc_mem_operations = { |
899 | .llseek = mem_lseek, | 831 | .llseek = mem_lseek, |
900 | .read = mem_read, | 832 | .read = mem_read, |
901 | .write = mem_write, | 833 | .write = mem_write, |
902 | .open = mem_open, | 834 | .open = mem_open, |
835 | .release = mem_release, | ||
903 | }; | 836 | }; |
904 | 837 | ||
905 | static ssize_t environ_read(struct file *file, char __user *buf, | 838 | static ssize_t environ_read(struct file *file, char __user *buf, |