Merge branch 'work.lookup' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs
authorLinus Torvalds <torvalds@linux-foundation.org>
Fri, 8 Jun 2018 16:56:38 +0000 (09:56 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Fri, 8 Jun 2018 16:56:38 +0000 (09:56 -0700)
Pull proc_fill_cache regression fix from Al Viro:
 "Regression fix for proc_fill_cache() braino introduced when switching
  instantiate() callback to d_splice_alias()"

* 'work.lookup' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs:
  fix proc_fill_cache() in case of d_alloc_parallel() failure

1  2 
fs/proc/base.c

diff --combined fs/proc/base.c
@@@ -213,14 -213,10 +213,14 @@@ static ssize_t proc_pid_cmdline_read(st
        char *page;
        unsigned long count = _count;
        unsigned long arg_start, arg_end, env_start, env_end;
 -      unsigned long len1, len2, len;
 -      unsigned long p;
 +      unsigned long len1, len2;
 +      char __user *buf0 = buf;
 +      struct {
 +              unsigned long p;
 +              unsigned long len;
 +      } cmdline[2];
        char c;
 -      ssize_t rv;
 +      int rv;
  
        BUG_ON(*pos < 0);
  
                goto out_mmput;
        }
  
 -      down_read(&mm->mmap_sem);
 +      spin_lock(&mm->arg_lock);
        arg_start = mm->arg_start;
        arg_end = mm->arg_end;
        env_start = mm->env_start;
        env_end = mm->env_end;
 -      up_read(&mm->mmap_sem);
 +      spin_unlock(&mm->arg_lock);
  
        BUG_ON(arg_start > arg_end);
        BUG_ON(env_start > env_end);
        len2 = env_end - env_start;
  
        /* Empty ARGV. */
 -      if (len1 == 0) {
 -              rv = 0;
 -              goto out_free_page;
 -      }
 +      if (len1 == 0)
 +              goto end;
 +
        /*
         * Inherently racy -- command line shares address space
         * with code and data.
         */
 -      rv = access_remote_vm(mm, arg_end - 1, &c, 1, 0);
 -      if (rv <= 0)
 -              goto out_free_page;
 -
 -      rv = 0;
 +      if (access_remote_vm(mm, arg_end - 1, &c, 1, FOLL_ANON) != 1)
 +              goto end;
  
 +      cmdline[0].p = arg_start;
 +      cmdline[0].len = len1;
        if (c == '\0') {
                /* Command line (set of strings) occupies whole ARGV. */
 -              if (len1 <= *pos)
 -                      goto out_free_page;
 -
 -              p = arg_start + *pos;
 -              len = len1 - *pos;
 -              while (count > 0 && len > 0) {
 -                      unsigned int _count;
 -                      int nr_read;
 -
 -                      _count = min3(count, len, PAGE_SIZE);
 -                      nr_read = access_remote_vm(mm, p, page, _count, 0);
 -                      if (nr_read < 0)
 -                              rv = nr_read;
 -                      if (nr_read <= 0)
 -                              goto out_free_page;
 -
 -                      if (copy_to_user(buf, page, nr_read)) {
 -                              rv = -EFAULT;
 -                              goto out_free_page;
 -                      }
 -
 -                      p       += nr_read;
 -                      len     -= nr_read;
 -                      buf     += nr_read;
 -                      count   -= nr_read;
 -                      rv      += nr_read;
 -              }
 +              cmdline[1].len = 0;
        } else {
                /*
                 * Command line (1 string) occupies ARGV and
                 * extends into ENVP.
                 */
 -              struct {
 -                      unsigned long p;
 -                      unsigned long len;
 -              } cmdline[2] = {
 -                      { .p = arg_start, .len = len1 },
 -                      { .p = env_start, .len = len2 },
 -              };
 +              cmdline[1].p = env_start;
 +              cmdline[1].len = len2;
 +      }
 +
 +      {
                loff_t pos1 = *pos;
                unsigned int i;
  
                        i++;
                }
                while (i < 2) {
 +                      unsigned long p;
 +                      unsigned long len;
 +
                        p = cmdline[i].p + pos1;
                        len = cmdline[i].len - pos1;
                        while (count > 0 && len > 0) {
 -                              unsigned int _count, l;
 -                              int nr_read;
 -                              bool final;
 -
 -                              _count = min3(count, len, PAGE_SIZE);
 -                              nr_read = access_remote_vm(mm, p, page, _count, 0);
 -                              if (nr_read < 0)
 -                                      rv = nr_read;
 -                              if (nr_read <= 0)
 -                                      goto out_free_page;
 +                              unsigned int nr_read, nr_write;
 +
 +                              nr_read = min3(count, len, PAGE_SIZE);
 +                              nr_read = access_remote_vm(mm, p, page, nr_read, FOLL_ANON);
 +                              if (nr_read == 0)
 +                                      goto end;
  
                                /*
                                 * Command line can be shorter than whole ARGV
                                 * even if last "marker" byte says it is not.
                                 */
 -                              final = false;
 -                              l = strnlen(page, nr_read);
 -                              if (l < nr_read) {
 -                                      nr_read = l;
 -                                      final = true;
 -                              }
 +                              if (c == '\0')
 +                                      nr_write = nr_read;
 +                              else
 +                                      nr_write = strnlen(page, nr_read);
  
 -                              if (copy_to_user(buf, page, nr_read)) {
 +                              if (copy_to_user(buf, page, nr_write)) {
                                        rv = -EFAULT;
                                        goto out_free_page;
                                }
  
 -                              p       += nr_read;
 -                              len     -= nr_read;
 -                              buf     += nr_read;
 -                              count   -= nr_read;
 -                              rv      += nr_read;
 +                              p       += nr_write;
 +                              len     -= nr_write;
 +                              buf     += nr_write;
 +                              count   -= nr_write;
  
 -                              if (final)
 -                                      goto out_free_page;
 +                              if (nr_write < nr_read)
 +                                      goto end;
                        }
  
                        /* Only first chunk can be read partially. */
                }
        }
  
 +end:
 +      *pos += buf - buf0;
 +      rv = buf - buf0;
  out_free_page:
        free_page((unsigned long)page);
  out_mmput:
        mmput(mm);
 -      if (rv > 0)
 -              *pos += rv;
        return rv;
  }
  
@@@ -401,6 -430,7 +401,6 @@@ static int proc_pid_stack(struct seq_fi
        struct stack_trace trace;
        unsigned long *entries;
        int err;
 -      int i;
  
        entries = kmalloc(MAX_STACK_TRACE_DEPTH * sizeof(*entries), GFP_KERNEL);
        if (!entries)
  
        err = lock_trace(task);
        if (!err) {
 +              unsigned int i;
 +
                save_stack_trace_tsk(task, &trace);
  
                for (i = 0; i < trace.nr_entries; i++) {
@@@ -670,7 -698,7 +670,7 @@@ static bool has_pid_permissions(struct 
  
  static int proc_pid_permission(struct inode *inode, int mask)
  {
 -      struct pid_namespace *pid = inode->i_sb->s_fs_info;
 +      struct pid_namespace *pid = proc_pid_ns(inode);
        struct task_struct *task;
        bool has_perms;
  
@@@ -705,11 -733,13 +705,11 @@@ static const struct inode_operations pr
  static int proc_single_show(struct seq_file *m, void *v)
  {
        struct inode *inode = m->private;
 -      struct pid_namespace *ns;
 -      struct pid *pid;
 +      struct pid_namespace *ns = proc_pid_ns(inode);
 +      struct pid *pid = proc_pid(inode);
        struct task_struct *task;
        int ret;
  
 -      ns = inode->i_sb->s_fs_info;
 -      pid = proc_pid(inode);
        task = get_pid_task(pid, PIDTYPE_PID);
        if (!task)
                return -ESRCH;
@@@ -899,10 -929,10 +899,10 @@@ static ssize_t environ_read(struct fil
        if (!mmget_not_zero(mm))
                goto free;
  
 -      down_read(&mm->mmap_sem);
 +      spin_lock(&mm->arg_lock);
        env_start = mm->env_start;
        env_end = mm->env_end;
 -      up_read(&mm->mmap_sem);
 +      spin_unlock(&mm->arg_lock);
  
        while (count > 0) {
                size_t this_len, max_len;
                max_len = min_t(size_t, PAGE_SIZE, count);
                this_len = min(max_len, this_len);
  
 -              retval = access_remote_vm(mm, (env_start + src), page, this_len, 0);
 +              retval = access_remote_vm(mm, (env_start + src), page, this_len, FOLL_ANON);
  
                if (retval <= 0) {
                        ret = retval;
@@@ -1380,7 -1410,7 +1380,7 @@@ static const struct file_operations pro
  static int sched_show(struct seq_file *m, void *v)
  {
        struct inode *inode = m->private;
 -      struct pid_namespace *ns = inode->i_sb->s_fs_info;
 +      struct pid_namespace *ns = proc_pid_ns(inode);
        struct task_struct *p;
  
        p = get_proc_task(inode);
@@@ -1535,8 -1565,9 +1535,8 @@@ static int comm_show(struct seq_file *m
        if (!p)
                return -ESRCH;
  
 -      task_lock(p);
 -      seq_printf(m, "%s\n", p->comm);
 -      task_unlock(p);
 +      proc_task_name(m, p, false);
 +      seq_putc(m, '\n');
  
        put_task_struct(p);
  
@@@ -1662,12 -1693,6 +1662,12 @@@ void task_dump_owner(struct task_struc
        kuid_t uid;
        kgid_t gid;
  
 +      if (unlikely(task->flags & PF_KTHREAD)) {
 +              *ruid = GLOBAL_ROOT_UID;
 +              *rgid = GLOBAL_ROOT_GID;
 +              return;
 +      }
 +
        /* Default to the tasks effective ownership */
        rcu_read_lock();
        cred = __task_cred(task);
@@@ -1751,14 -1776,14 +1751,14 @@@ int pid_getattr(const struct path *path
                u32 request_mask, unsigned int query_flags)
  {
        struct inode *inode = d_inode(path->dentry);
 +      struct pid_namespace *pid = proc_pid_ns(inode);
        struct task_struct *task;
 -      struct pid_namespace *pid = path->dentry->d_sb->s_fs_info;
  
        generic_fillattr(inode, stat);
  
 -      rcu_read_lock();
        stat->uid = GLOBAL_ROOT_UID;
        stat->gid = GLOBAL_ROOT_GID;
 +      rcu_read_lock();
        task = pid_task(proc_pid(inode), PIDTYPE_PID);
        if (task) {
                if (!has_pid_permissions(pid, task, HIDEPID_INVISIBLE)) {
@@@ -1847,7 -1872,7 +1847,7 @@@ const struct dentry_operations pid_dent
   * by stat.
   */
  bool proc_fill_cache(struct file *file, struct dir_context *ctx,
 -      const char *name, int len,
 +      const char *name, unsigned int len,
        instantiate_t instantiate, struct task_struct *task, const void *ptr)
  {
        struct dentry *child, *dir = file->f_path.dentry;
                        struct dentry *res;
                        res = instantiate(child, task, ptr);
                        d_lookup_done(child);
-                       if (IS_ERR(res))
-                               goto end_instantiate;
                        if (unlikely(res)) {
                                dput(child);
                                child = res;
+                               if (IS_ERR(child))
+                                       goto end_instantiate;
                        }
                }
        }
        inode = d_inode(child);
        ino = inode->i_ino;
        type = inode->i_mode >> 12;
- end_instantiate:
        dput(child);
+ end_instantiate:
        return dir_emit(ctx, name, len, ino, type);
  }
  
@@@ -2309,7 -2334,7 +2309,7 @@@ static int proc_timers_open(struct inod
                return -ENOMEM;
  
        tp->pid = proc_pid(inode);
 -      tp->ns = inode->i_sb->s_fs_info;
 +      tp->ns = proc_pid_ns(inode);
        return 0;
  }
  
@@@ -3199,7 -3224,7 +3199,7 @@@ retry
  int proc_pid_readdir(struct file *file, struct dir_context *ctx)
  {
        struct tgid_iter iter;
 -      struct pid_namespace *ns = file_inode(file)->i_sb->s_fs_info;
 +      struct pid_namespace *ns = proc_pid_ns(file_inode(file));
        loff_t pos = ctx->pos;
  
        if (pos >= PID_MAX_LIMIT + TGID_OFFSET)
             iter.task;
             iter.tgid += 1, iter = next_tgid(ns, iter)) {
                char name[10 + 1];
 -              int len;
 +              unsigned int len;
  
                cond_resched();
                if (!has_pid_permissions(ns, iter.task, HIDEPID_INVISIBLE))
@@@ -3543,14 -3568,14 +3543,14 @@@ static int proc_task_readdir(struct fil
        /* f_version caches the tgid value that the last readdir call couldn't
         * return. lseek aka telldir automagically resets f_version to 0.
         */
 -      ns = inode->i_sb->s_fs_info;
 +      ns = proc_pid_ns(inode);
        tid = (int)file->f_version;
        file->f_version = 0;
        for (task = first_tid(proc_pid(inode), tid, ctx->pos - 2, ns);
             task;
             task = next_tid(task), ctx->pos++) {
                char name[10 + 1];
 -              int len;
 +              unsigned int len;
                tid = task_pid_nr_ns(task, ns);
                len = snprintf(name, sizeof(name), "%u", tid);
                if (!proc_fill_cache(file, ctx, name, len,