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; |
