diff options
Diffstat (limited to 'fs/proc')
| -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 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 | ||
| 198 | static struct mm_struct *__check_mem_permission(struct task_struct *task) | 198 | static 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 | */ | ||
| 237 | static 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 | |||
| 256 | struct 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 | ||
| 218 | struct mm_struct *mm_for_maps(struct task_struct *task) | ||
| 219 | { | ||
| 220 | return mm_access(task, PTRACE_MODE_READ); | ||
| 221 | } | ||
| 222 | |||
| 276 | static int proc_pid_cmdline(struct task_struct *task, char * buffer) | 223 | static 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 | ||
| 842 | static int mem_open(struct inode* inode, struct file* file) | 789 | static 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 | ||
| 850 | static ssize_t mem_read(struct file * file, char __user * buf, | 810 | static 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 | ||
| 902 | out_put: | ||
| 903 | mmput(mm); | ||
| 904 | out_free: | ||
| 905 | free_page((unsigned long) page); | 850 | free_page((unsigned long) page); |
| 906 | out: | ||
| 907 | put_task_struct(task); | ||
| 908 | out_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 | ||
| 961 | out_mm: | ||
| 962 | mmput(mm); | ||
| 963 | out_free: | ||
| 964 | free_page((unsigned long) page); | 891 | free_page((unsigned long) page); |
| 965 | out_task: | ||
| 966 | put_task_struct(task); | ||
| 967 | out_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 | ||
| 911 | static 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 | |||
| 987 | static const struct file_operations proc_mem_operations = { | 919 | static 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 | ||
| 994 | static ssize_t environ_read(struct file *file, char __user *buf, | 927 | static ssize_t environ_read(struct file *file, char __user *buf, |
