diff options
Diffstat (limited to 'fs/proc/base.c')
-rw-r--r-- | fs/proc/base.c | 114 |
1 files changed, 105 insertions, 9 deletions
diff --git a/fs/proc/base.c b/fs/proc/base.c index c5e412a00b17..808cbdc193d3 100644 --- a/fs/proc/base.c +++ b/fs/proc/base.c | |||
@@ -56,6 +56,7 @@ | |||
56 | #include <linux/init.h> | 56 | #include <linux/init.h> |
57 | #include <linux/capability.h> | 57 | #include <linux/capability.h> |
58 | #include <linux/file.h> | 58 | #include <linux/file.h> |
59 | #include <linux/fdtable.h> | ||
59 | #include <linux/string.h> | 60 | #include <linux/string.h> |
60 | #include <linux/seq_file.h> | 61 | #include <linux/seq_file.h> |
61 | #include <linux/namei.h> | 62 | #include <linux/namei.h> |
@@ -195,12 +196,32 @@ static int proc_root_link(struct inode *inode, struct path *path) | |||
195 | return result; | 196 | return result; |
196 | } | 197 | } |
197 | 198 | ||
198 | #define MAY_PTRACE(task) \ | 199 | /* |
199 | (task == current || \ | 200 | * Return zero if current may access user memory in @task, -error if not. |
200 | (task->parent == current && \ | 201 | */ |
201 | (task->ptrace & PT_PTRACED) && \ | 202 | static int check_mem_permission(struct task_struct *task) |
202 | (task_is_stopped_or_traced(task)) && \ | 203 | { |
203 | security_ptrace(current,task) == 0)) | 204 | /* |
205 | * A task can always look at itself, in case it chooses | ||
206 | * to use system calls instead of load instructions. | ||
207 | */ | ||
208 | if (task == current) | ||
209 | return 0; | ||
210 | |||
211 | /* | ||
212 | * If current is actively ptrace'ing, and would also be | ||
213 | * permitted to freshly attach with ptrace now, permit it. | ||
214 | */ | ||
215 | if (task->parent == current && (task->ptrace & PT_PTRACED) && | ||
216 | task_is_stopped_or_traced(task) && | ||
217 | ptrace_may_attach(task)) | ||
218 | return 0; | ||
219 | |||
220 | /* | ||
221 | * Noone else is allowed. | ||
222 | */ | ||
223 | return -EPERM; | ||
224 | } | ||
204 | 225 | ||
205 | struct mm_struct *mm_for_maps(struct task_struct *task) | 226 | struct mm_struct *mm_for_maps(struct task_struct *task) |
206 | { | 227 | { |
@@ -722,7 +743,7 @@ static ssize_t mem_read(struct file * file, char __user * buf, | |||
722 | if (!task) | 743 | if (!task) |
723 | goto out_no_task; | 744 | goto out_no_task; |
724 | 745 | ||
725 | if (!MAY_PTRACE(task) || !ptrace_may_attach(task)) | 746 | if (check_mem_permission(task)) |
726 | goto out; | 747 | goto out; |
727 | 748 | ||
728 | ret = -ENOMEM; | 749 | ret = -ENOMEM; |
@@ -748,7 +769,7 @@ static ssize_t mem_read(struct file * file, char __user * buf, | |||
748 | 769 | ||
749 | this_len = (count > PAGE_SIZE) ? PAGE_SIZE : count; | 770 | this_len = (count > PAGE_SIZE) ? PAGE_SIZE : count; |
750 | retval = access_process_vm(task, src, page, this_len, 0); | 771 | retval = access_process_vm(task, src, page, this_len, 0); |
751 | if (!retval || !MAY_PTRACE(task) || !ptrace_may_attach(task)) { | 772 | if (!retval || check_mem_permission(task)) { |
752 | if (!ret) | 773 | if (!ret) |
753 | ret = -EIO; | 774 | ret = -EIO; |
754 | break; | 775 | break; |
@@ -792,7 +813,7 @@ static ssize_t mem_write(struct file * file, const char __user *buf, | |||
792 | if (!task) | 813 | if (!task) |
793 | goto out_no_task; | 814 | goto out_no_task; |
794 | 815 | ||
795 | if (!MAY_PTRACE(task) || !ptrace_may_attach(task)) | 816 | if (check_mem_permission(task)) |
796 | goto out; | 817 | goto out; |
797 | 818 | ||
798 | copied = -ENOMEM; | 819 | copied = -ENOMEM; |
@@ -1181,6 +1202,81 @@ static const struct file_operations proc_pid_sched_operations = { | |||
1181 | 1202 | ||
1182 | #endif | 1203 | #endif |
1183 | 1204 | ||
1205 | /* | ||
1206 | * We added or removed a vma mapping the executable. The vmas are only mapped | ||
1207 | * during exec and are not mapped with the mmap system call. | ||
1208 | * Callers must hold down_write() on the mm's mmap_sem for these | ||
1209 | */ | ||
1210 | void added_exe_file_vma(struct mm_struct *mm) | ||
1211 | { | ||
1212 | mm->num_exe_file_vmas++; | ||
1213 | } | ||
1214 | |||
1215 | void removed_exe_file_vma(struct mm_struct *mm) | ||
1216 | { | ||
1217 | mm->num_exe_file_vmas--; | ||
1218 | if ((mm->num_exe_file_vmas == 0) && mm->exe_file){ | ||
1219 | fput(mm->exe_file); | ||
1220 | mm->exe_file = NULL; | ||
1221 | } | ||
1222 | |||
1223 | } | ||
1224 | |||
1225 | void set_mm_exe_file(struct mm_struct *mm, struct file *new_exe_file) | ||
1226 | { | ||
1227 | if (new_exe_file) | ||
1228 | get_file(new_exe_file); | ||
1229 | if (mm->exe_file) | ||
1230 | fput(mm->exe_file); | ||
1231 | mm->exe_file = new_exe_file; | ||
1232 | mm->num_exe_file_vmas = 0; | ||
1233 | } | ||
1234 | |||
1235 | struct file *get_mm_exe_file(struct mm_struct *mm) | ||
1236 | { | ||
1237 | struct file *exe_file; | ||
1238 | |||
1239 | /* We need mmap_sem to protect against races with removal of | ||
1240 | * VM_EXECUTABLE vmas */ | ||
1241 | down_read(&mm->mmap_sem); | ||
1242 | exe_file = mm->exe_file; | ||
1243 | if (exe_file) | ||
1244 | get_file(exe_file); | ||
1245 | up_read(&mm->mmap_sem); | ||
1246 | return exe_file; | ||
1247 | } | ||
1248 | |||
1249 | void dup_mm_exe_file(struct mm_struct *oldmm, struct mm_struct *newmm) | ||
1250 | { | ||
1251 | /* It's safe to write the exe_file pointer without exe_file_lock because | ||
1252 | * this is called during fork when the task is not yet in /proc */ | ||
1253 | newmm->exe_file = get_mm_exe_file(oldmm); | ||
1254 | } | ||
1255 | |||
1256 | static int proc_exe_link(struct inode *inode, struct path *exe_path) | ||
1257 | { | ||
1258 | struct task_struct *task; | ||
1259 | struct mm_struct *mm; | ||
1260 | struct file *exe_file; | ||
1261 | |||
1262 | task = get_proc_task(inode); | ||
1263 | if (!task) | ||
1264 | return -ENOENT; | ||
1265 | mm = get_task_mm(task); | ||
1266 | put_task_struct(task); | ||
1267 | if (!mm) | ||
1268 | return -ENOENT; | ||
1269 | exe_file = get_mm_exe_file(mm); | ||
1270 | mmput(mm); | ||
1271 | if (exe_file) { | ||
1272 | *exe_path = exe_file->f_path; | ||
1273 | path_get(&exe_file->f_path); | ||
1274 | fput(exe_file); | ||
1275 | return 0; | ||
1276 | } else | ||
1277 | return -ENOENT; | ||
1278 | } | ||
1279 | |||
1184 | static void *proc_pid_follow_link(struct dentry *dentry, struct nameidata *nd) | 1280 | static void *proc_pid_follow_link(struct dentry *dentry, struct nameidata *nd) |
1185 | { | 1281 | { |
1186 | struct inode *inode = dentry->d_inode; | 1282 | struct inode *inode = dentry->d_inode; |