close race in /proc/*/environ, CVE-2011-1020
[linux-flexiantxendom0-natty.git] / fs / proc / base.c
index 08cba2c..3803523 100644 (file)
@@ -225,15 +225,17 @@ static int check_mem_permission(struct task_struct *task)
 struct mm_struct *mm_for_maps(struct task_struct *task)
 {
        struct mm_struct *mm;
+       int err;
 
-       if (mutex_lock_killable(&task->signal->cred_guard_mutex))
-               return NULL;
+       err =  mutex_lock_killable(&task->signal->cred_guard_mutex);
+       if (err)
+               return ERR_PTR(err);
 
        mm = get_task_mm(task);
        if (mm && mm != current->mm &&
                        !ptrace_may_access(task, PTRACE_MODE_READ)) {
                mmput(mm);
-               mm = NULL;
+               mm = ERR_PTR(-EACCES);
        }
        mutex_unlock(&task->signal->cred_guard_mutex);
 
@@ -340,7 +342,7 @@ static int proc_pid_stack(struct seq_file *m, struct pid_namespace *ns,
        save_stack_trace_tsk(task, &trace);
 
        for (i = 0; i < trace.nr_entries; i++) {
-               seq_printf(m, "[<%p>] %pS\n",
+               seq_printf(m, "[<%pK>] %pS\n",
                           (void *)entries[i], (void *)entries[i]);
        }
        kfree(entries);
@@ -373,26 +375,20 @@ static int lstats_show_proc(struct seq_file *m, void *v)
                return -ESRCH;
        seq_puts(m, "Latency Top version : v0.1\n");
        for (i = 0; i < 32; i++) {
-               if (task->latency_record[i].backtrace[0]) {
+               struct latency_record *lr = &task->latency_record[i];
+               if (lr->backtrace[0]) {
                        int q;
-                       seq_printf(m, "%i %li %li ",
-                               task->latency_record[i].count,
-                               task->latency_record[i].time,
-                               task->latency_record[i].max);
+                       seq_printf(m, "%i %li %li",
+                                  lr->count, lr->time, lr->max);
                        for (q = 0; q < LT_BACKTRACEDEPTH; q++) {
-                               char sym[KSYM_SYMBOL_LEN];
-                               char *c;
-                               if (!task->latency_record[i].backtrace[q])
+                               unsigned long bt = lr->backtrace[q];
+                               if (!bt)
                                        break;
-                               if (task->latency_record[i].backtrace[q] == ULONG_MAX)
+                               if (bt == ULONG_MAX)
                                        break;
-                               sprint_symbol(sym, task->latency_record[i].backtrace[q]);
-                               c = strchr(sym, '+');
-                               if (c)
-                                       *c = 0;
-                               seq_printf(m, "%s ", sym);
+                               seq_printf(m, " %ps", (void *)bt);
                        }
-                       seq_printf(m, "\n");
+                       seq_putc(m, '\n');
                }
 
        }
@@ -751,14 +747,7 @@ static int proc_single_show(struct seq_file *m, void *v)
 
 static int proc_single_open(struct inode *inode, struct file *filp)
 {
-       int ret;
-       ret = single_open(filp, proc_single_show, NULL);
-       if (!ret) {
-               struct seq_file *m = filp->private_data;
-
-               m->private = inode;
-       }
-       return ret;
+       return single_open(filp, proc_single_show, inode);
 }
 
 static const struct file_operations proc_single_file_operations = {
@@ -930,20 +919,18 @@ static ssize_t environ_read(struct file *file, char __user *buf,
        if (!task)
                goto out_no_task;
 
-       if (!ptrace_may_access(task, PTRACE_MODE_READ))
-               goto out;
-
        ret = -ENOMEM;
        page = (char *)__get_free_page(GFP_TEMPORARY);
        if (!page)
                goto out;
 
-       ret = 0;
 
-       mm = get_task_mm(task);
-       if (!mm)
+       mm = mm_for_maps(task);
+       ret = PTR_ERR(mm);
+       if (!mm || IS_ERR(mm))
                goto out_free;
 
+       ret = 0;
        while (count > 0) {
                int this_len, retval, max_len;
 
@@ -1164,7 +1151,7 @@ static ssize_t oom_score_adj_write(struct file *file, const char __user *buf,
                goto err_task_lock;
        }
 
-       if (oom_score_adj < task->signal->oom_score_adj &&
+       if (oom_score_adj < task->signal->oom_score_adj_min &&
                        !capable(CAP_SYS_RESOURCE)) {
                err = -EACCES;
                goto err_sighand;
@@ -1177,6 +1164,8 @@ static ssize_t oom_score_adj_write(struct file *file, const char __user *buf,
                        atomic_dec(&task->mm->oom_disable_count);
        }
        task->signal->oom_score_adj = oom_score_adj;
+       if (has_capability_noaudit(current, CAP_SYS_RESOURCE))
+               task->signal->oom_score_adj_min = oom_score_adj;
        /*
         * Scale /proc/pid/oom_adj appropriately ensuring that OOM_DISABLE is
         * always attainable.
@@ -1386,15 +1375,7 @@ sched_write(struct file *file, const char __user *buf,
 
 static int sched_open(struct inode *inode, struct file *filp)
 {
-       int ret;
-
-       ret = single_open(filp, sched_show, NULL);
-       if (!ret) {
-               struct seq_file *m = filp->private_data;
-
-               m->private = inode;
-       }
-       return ret;
+       return single_open(filp, sched_show, inode);
 }
 
 static const struct file_operations proc_pid_sched_operations = {
@@ -1530,15 +1511,7 @@ static int comm_show(struct seq_file *m, void *v)
 
 static int comm_open(struct inode *inode, struct file *filp)
 {
-       int ret;
-
-       ret = single_open(filp, comm_show, NULL);
-       if (!ret) {
-               struct seq_file *m = filp->private_data;
-
-               m->private = inode;
-       }
-       return ret;
+       return single_open(filp, comm_show, inode);
 }
 
 static const struct file_operations proc_pid_set_comm_operations = {
@@ -1795,10 +1768,16 @@ static int pid_getattr(struct vfsmount *mnt, struct dentry *dentry, struct kstat
  */
 static int pid_revalidate(struct dentry *dentry, struct nameidata *nd)
 {
-       struct inode *inode = dentry->d_inode;
-       struct task_struct *task = get_proc_task(inode);
+       struct inode *inode;
+       struct task_struct *task;
        const struct cred *cred;
 
+       if (nd && nd->flags & LOOKUP_RCU)
+               return -ECHILD;
+
+       inode = dentry->d_inode;
+       task = get_proc_task(inode);
+
        if (task) {
                if ((inode->i_mode == (S_IFDIR|S_IRUGO|S_IXUGO)) ||
                    task_dumpable(task)) {
@@ -1820,7 +1799,7 @@ static int pid_revalidate(struct dentry *dentry, struct nameidata *nd)
        return 0;
 }
 
-static int pid_delete_dentry(struct dentry * dentry)
+static int pid_delete_dentry(const struct dentry * dentry)
 {
        /* Is the task we represent dead?
         * If so, then don't put the dentry on the lru list,
@@ -1964,12 +1943,19 @@ static int proc_fd_link(struct inode *inode, struct path *path)
 
 static int tid_fd_revalidate(struct dentry *dentry, struct nameidata *nd)
 {
-       struct inode *inode = dentry->d_inode;
-       struct task_struct *task = get_proc_task(inode);
-       int fd = proc_fd(inode);
+       struct inode *inode;
+       struct task_struct *task;
+       int fd;
        struct files_struct *files;
        const struct cred *cred;
 
+       if (nd && nd->flags & LOOKUP_RCU)
+               return -ECHILD;
+
+       inode = dentry->d_inode;
+       task = get_proc_task(inode);
+       fd = proc_fd(inode);
+
        if (task) {
                files = get_files_struct(task);
                if (files) {
@@ -2045,7 +2031,7 @@ static struct dentry *proc_fd_instantiate(struct inode *dir,
        inode->i_op = &proc_pid_link_inode_operations;
        inode->i_size = 64;
        ei->op.proc_get_link = proc_fd_link;
-       dentry->d_op = &tid_fd_dentry_operations;
+       d_set_d_op(dentry, &tid_fd_dentry_operations);
        d_add(dentry, inode);
        /* Close the race of the process dying before we return the dentry */
        if (tid_fd_revalidate(dentry, NULL))
@@ -2177,11 +2163,13 @@ static const struct file_operations proc_fd_operations = {
  * /proc/pid/fd needs a special permission handler so that a process can still
  * access /proc/self/fd after it has executed a setuid().
  */
-static int proc_fd_permission(struct inode *inode, int mask)
+static int proc_fd_permission(struct inode *inode, int mask, unsigned int flags)
 {
        int rv;
 
-       rv = generic_permission(inode, mask, NULL);
+       if (flags & IPERM_FLAG_RCU)
+               return -ECHILD;
+       rv = generic_permission(inode, mask, flags, NULL);
        if (rv == 0)
                return 0;
        if (task_pid(current) == proc_pid(inode))
@@ -2213,7 +2201,7 @@ static struct dentry *proc_fdinfo_instantiate(struct inode *dir,
        ei->fd = fd;
        inode->i_mode = S_IFREG | S_IRUSR;
        inode->i_fop = &proc_fdinfo_file_operations;
-       dentry->d_op = &tid_fd_dentry_operations;
+       d_set_d_op(dentry, &tid_fd_dentry_operations);
        d_add(dentry, inode);
        /* Close the race of the process dying before we return the dentry */
        if (tid_fd_revalidate(dentry, NULL))
@@ -2272,7 +2260,7 @@ static struct dentry *proc_pident_instantiate(struct inode *dir,
        if (p->fop)
                inode->i_fop = p->fop;
        ei->op = p->op;
-       dentry->d_op = &pid_dentry_operations;
+       d_set_d_op(dentry, &pid_dentry_operations);
        d_add(dentry, inode);
        /* Close the race of the process dying before we return the dentry */
        if (pid_revalidate(dentry, NULL))
@@ -2632,29 +2620,6 @@ static const struct pid_entry proc_base_stuff[] = {
                &proc_self_inode_operations, NULL, {}),
 };
 
-/*
- *     Exceptional case: normally we are not allowed to unhash a busy
- * directory. In this case, however, we can do it - no aliasing problems
- * due to the way we treat inodes.
- */
-static int proc_base_revalidate(struct dentry *dentry, struct nameidata *nd)
-{
-       struct inode *inode = dentry->d_inode;
-       struct task_struct *task = get_proc_task(inode);
-       if (task) {
-               put_task_struct(task);
-               return 1;
-       }
-       d_drop(dentry);
-       return 0;
-}
-
-static const struct dentry_operations proc_base_dentry_operations =
-{
-       .d_revalidate   = proc_base_revalidate,
-       .d_delete       = pid_delete_dentry,
-};
-
 static struct dentry *proc_base_instantiate(struct inode *dir,
        struct dentry *dentry, struct task_struct *task, const void *ptr)
 {
@@ -2691,7 +2656,6 @@ static struct dentry *proc_base_instantiate(struct inode *dir,
        if (p->fop)
                inode->i_fop = p->fop;
        ei->op = p->op;
-       dentry->d_op = &proc_base_dentry_operations;
        d_add(dentry, inode);
        error = NULL;
 out:
@@ -2833,7 +2797,7 @@ static const struct pid_entry tgid_base_stuff[] = {
 #ifdef CONFIG_PROC_PAGE_MONITOR
        REG("clear_refs", S_IWUSR, proc_clear_refs_operations),
        REG("smaps",      S_IRUGO, proc_smaps_operations),
-       REG("pagemap",    S_IRUSR, proc_pagemap_operations),
+       REG("pagemap",    S_IRUGO, proc_pagemap_operations),
 #endif
 #ifdef CONFIG_SECURITY
        DIR("attr",       S_IRUGO|S_IXUGO, proc_attr_dir_inode_operations, proc_attr_dir_operations),
@@ -3005,7 +2969,7 @@ static struct dentry *proc_pid_instantiate(struct inode *dir,
        inode->i_nlink = 2 + pid_entry_count_dirs(tgid_base_stuff,
                ARRAY_SIZE(tgid_base_stuff));
 
-       dentry->d_op = &pid_dentry_operations;
+       d_set_d_op(dentry, &pid_dentry_operations);
 
        d_add(dentry, inode);
        /* Close the race of the process dying before we return the dentry */
@@ -3102,11 +3066,16 @@ static int proc_pid_fill_cache(struct file *filp, void *dirent, filldir_t filldi
 /* for the /proc/ directory itself, after non-process stuff has been done */
 int proc_pid_readdir(struct file * filp, void * dirent, filldir_t filldir)
 {
-       unsigned int nr = filp->f_pos - FIRST_PROCESS_ENTRY;
-       struct task_struct *reaper = get_proc_task(filp->f_path.dentry->d_inode);
+       unsigned int nr;
+       struct task_struct *reaper;
        struct tgid_iter iter;
        struct pid_namespace *ns;
 
+       if (filp->f_pos >= PID_MAX_LIMIT + TGID_OFFSET)
+               goto out_no_task;
+       nr = filp->f_pos - FIRST_PROCESS_ENTRY;
+
+       reaper = get_proc_task(filp->f_path.dentry->d_inode);
        if (!reaper)
                goto out_no_task;
 
@@ -3169,7 +3138,7 @@ static const struct pid_entry tid_base_stuff[] = {
 #ifdef CONFIG_PROC_PAGE_MONITOR
        REG("clear_refs", S_IWUSR, proc_clear_refs_operations),
        REG("smaps",     S_IRUGO, proc_smaps_operations),
-       REG("pagemap",    S_IRUSR, proc_pagemap_operations),
+       REG("pagemap",    S_IRUGO, proc_pagemap_operations),
 #endif
 #ifdef CONFIG_SECURITY
        DIR("attr",      S_IRUGO|S_IXUGO, proc_attr_dir_inode_operations, proc_attr_dir_operations),
@@ -3248,7 +3217,7 @@ static struct dentry *proc_task_instantiate(struct inode *dir,
        inode->i_nlink = 2 + pid_entry_count_dirs(tid_base_stuff,
                ARRAY_SIZE(tid_base_stuff));
 
-       dentry->d_op = &pid_dentry_operations;
+       d_set_d_op(dentry, &pid_dentry_operations);
 
        d_add(dentry, inode);
        /* Close the race of the process dying before we return the dentry */