]> git.proxmox.com Git - mirror_ubuntu-eoan-kernel.git/blobdiff - fs/proc/base.c
proc: simpler iterations for /proc/*/cmdline
[mirror_ubuntu-eoan-kernel.git] / fs / proc / base.c
index 4e35593546b1b24915163d7a936b2a39e1d8d680..3fcca77d45f1e758cf3a9b14c801beb22a2a2f63 100644 (file)
@@ -215,8 +215,9 @@ static ssize_t proc_pid_cmdline_read(struct file *file, char __user *buf,
        unsigned long arg_start, arg_end, env_start, env_end;
        unsigned long len1, len2, len;
        unsigned long p;
+       char __user *buf0 = buf;
        char c;
-       ssize_t rv;
+       int rv;
 
        BUG_ON(*pos < 0);
 
@@ -239,12 +240,12 @@ static ssize_t proc_pid_cmdline_read(struct file *file, char __user *buf,
                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);
@@ -253,37 +254,30 @@ static ssize_t proc_pid_cmdline_read(struct file *file, char __user *buf,
        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, FOLL_ANON);
-       if (rv <= 0)
-               goto out_free_page;
-
-       rv = 0;
+       if (access_remote_vm(mm, arg_end - 1, &c, 1, FOLL_ANON) != 1)
+               goto end;
 
        if (c == '\0') {
                /* Command line (set of strings) occupies whole ARGV. */
                if (len1 <= *pos)
-                       goto out_free_page;
+                       goto end;
 
                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, FOLL_ANON);
-                       if (nr_read < 0)
-                               rv = nr_read;
-                       if (nr_read <= 0)
-                               goto out_free_page;
+                       unsigned int nr_read;
+
+                       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;
 
                        if (copy_to_user(buf, page, nr_read)) {
                                rv = -EFAULT;
@@ -294,7 +288,6 @@ static ssize_t proc_pid_cmdline_read(struct file *file, char __user *buf,
                        len     -= nr_read;
                        buf     += nr_read;
                        count   -= nr_read;
-                       rv      += nr_read;
                }
        } else {
                /*
@@ -320,41 +313,31 @@ static ssize_t proc_pid_cmdline_read(struct file *file, char __user *buf,
                        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, FOLL_ANON);
-                               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;
-                               }
+                               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. */
@@ -363,12 +346,13 @@ static ssize_t proc_pid_cmdline_read(struct file *file, char __user *buf,
                }
        }
 
+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;
 }
 
@@ -927,10 +911,10 @@ static ssize_t environ_read(struct file *file, char __user *buf,
        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;
@@ -1563,9 +1547,8 @@ static int comm_show(struct seq_file *m, void *v)
        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);
 
@@ -1807,15 +1790,22 @@ int pid_getattr(const struct path *path, struct kstat *stat,
 /* dentry stuff */
 
 /*
- *     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.
- *
+ * Set <pid>/... inode ownership (can change due to setuid(), etc.)
+ */
+void pid_update_inode(struct task_struct *task, struct inode *inode)
+{
+       task_dump_owner(task, inode->i_mode, &inode->i_uid, &inode->i_gid);
+
+       inode->i_mode &= ~(S_ISUID | S_ISGID);
+       security_task_to_inode(task, inode);
+}
+
+/*
  * Rewrite the inode's ownerships here because the owning task may have
  * performed a setuid(), etc.
  *
  */
-int pid_revalidate(struct dentry *dentry, unsigned int flags)
+static int pid_revalidate(struct dentry *dentry, unsigned int flags)
 {
        struct inode *inode;
        struct task_struct *task;
@@ -1827,10 +1817,7 @@ int pid_revalidate(struct dentry *dentry, unsigned int flags)
        task = get_proc_task(inode);
 
        if (task) {
-               task_dump_owner(task, inode->i_mode, &inode->i_uid, &inode->i_gid);
-
-               inode->i_mode &= ~(S_ISUID | S_ISGID);
-               security_task_to_inode(task, inode);
+               pid_update_inode(task, inode);
                put_task_struct(task);
                return 1;
        }
@@ -1878,8 +1865,8 @@ bool proc_fill_cache(struct file *file, struct dir_context *ctx,
        struct dentry *child, *dir = file->f_path.dentry;
        struct qstr qname = QSTR_INIT(name, len);
        struct inode *inode;
-       unsigned type;
-       ino_t ino;
+       unsigned type = DT_UNKNOWN;
+       ino_t ino = 1;
 
        child = d_hash_and_lookup(dir, &qname);
        if (!child) {
@@ -1888,22 +1875,23 @@ bool proc_fill_cache(struct file *file, struct dir_context *ctx,
                if (IS_ERR(child))
                        goto end_instantiate;
                if (d_in_lookup(child)) {
-                       int err = instantiate(d_inode(dir), child, task, ptr);
+                       struct dentry *res;
+                       res = instantiate(child, task, ptr);
                        d_lookup_done(child);
-                       if (err < 0) {
-                               dput(child);
+                       if (IS_ERR(res))
                                goto end_instantiate;
+                       if (unlikely(res)) {
+                               dput(child);
+                               child = res;
                        }
                }
        }
        inode = d_inode(child);
        ino = inode->i_ino;
        type = inode->i_mode >> 12;
+end_instantiate:
        dput(child);
        return dir_emit(ctx, name, len, ino, type);
-
-end_instantiate:
-       return dir_emit(ctx, name, len, 1, DT_UNKNOWN);
 }
 
 /*
@@ -2065,19 +2053,19 @@ static const struct inode_operations proc_map_files_link_inode_operations = {
        .setattr        = proc_setattr,
 };
 
-static int
-proc_map_files_instantiate(struct inode *dir, struct dentry *dentry,
+static struct dentry *
+proc_map_files_instantiate(struct dentry *dentry,
                           struct task_struct *task, const void *ptr)
 {
        fmode_t mode = (fmode_t)(unsigned long)ptr;
        struct proc_inode *ei;
        struct inode *inode;
 
-       inode = proc_pid_make_inode(dir->i_sb, task, S_IFLNK |
+       inode = proc_pid_make_inode(dentry->d_sb, task, S_IFLNK |
                                    ((mode & FMODE_READ ) ? S_IRUSR : 0) |
                                    ((mode & FMODE_WRITE) ? S_IWUSR : 0));
        if (!inode)
-               return -ENOENT;
+               return ERR_PTR(-ENOENT);
 
        ei = PROC_I(inode);
        ei->op.proc_get_link = map_files_get_link;
@@ -2086,9 +2074,7 @@ proc_map_files_instantiate(struct inode *dir, struct dentry *dentry,
        inode->i_size = 64;
 
        d_set_d_op(dentry, &tid_map_files_dentry_operations);
-       d_add(dentry, inode);
-
-       return 0;
+       return d_splice_alias(inode, dentry);
 }
 
 static struct dentry *proc_map_files_lookup(struct inode *dir,
@@ -2097,19 +2083,19 @@ static struct dentry *proc_map_files_lookup(struct inode *dir,
        unsigned long vm_start, vm_end;
        struct vm_area_struct *vma;
        struct task_struct *task;
-       int result;
+       struct dentry *result;
        struct mm_struct *mm;
 
-       result = -ENOENT;
+       result = ERR_PTR(-ENOENT);
        task = get_proc_task(dir);
        if (!task)
                goto out;
 
-       result = -EACCES;
+       result = ERR_PTR(-EACCES);
        if (!ptrace_may_access(task, PTRACE_MODE_READ_FSCREDS))
                goto out_put_task;
 
-       result = -ENOENT;
+       result = ERR_PTR(-ENOENT);
        if (dname_to_vma_addr(dentry, &vm_start, &vm_end))
                goto out_put_task;
 
@@ -2123,7 +2109,7 @@ static struct dentry *proc_map_files_lookup(struct inode *dir,
                goto out_no_vma;
 
        if (vma->vm_file)
-               result = proc_map_files_instantiate(dir, dentry, task,
+               result = proc_map_files_instantiate(dentry, task,
                                (void *)(unsigned long)vma->vm_file->f_mode);
 
 out_no_vma:
@@ -2132,7 +2118,7 @@ out_no_vma:
 out_put_task:
        put_task_struct(task);
 out:
-       return ERR_PTR(result);
+       return result;
 }
 
 static const struct inode_operations proc_map_files_inode_operations = {
@@ -2433,16 +2419,16 @@ static const struct file_operations proc_pid_set_timerslack_ns_operations = {
        .release        = single_release,
 };
 
-static int proc_pident_instantiate(struct inode *dir,
-       struct dentry *dentry, struct task_struct *task, const void *ptr)
+static struct dentry *proc_pident_instantiate(struct dentry *dentry,
+       struct task_struct *task, const void *ptr)
 {
        const struct pid_entry *p = ptr;
        struct inode *inode;
        struct proc_inode *ei;
 
-       inode = proc_pid_make_inode(dir->i_sb, task, p->mode);
+       inode = proc_pid_make_inode(dentry->d_sb, task, p->mode);
        if (!inode)
-               goto out;
+               return ERR_PTR(-ENOENT);
 
        ei = PROC_I(inode);
        if (S_ISDIR(inode->i_mode))
@@ -2452,13 +2438,9 @@ static int proc_pident_instantiate(struct inode *dir,
        if (p->fop)
                inode->i_fop = p->fop;
        ei->op = p->op;
+       pid_update_inode(task, inode);
        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, 0))
-               return 0;
-out:
-       return -ENOENT;
+       return d_splice_alias(inode, dentry);
 }
 
 static struct dentry *proc_pident_lookup(struct inode *dir, 
@@ -2466,11 +2448,9 @@ static struct dentry *proc_pident_lookup(struct inode *dir,
                                         const struct pid_entry *ents,
                                         unsigned int nents)
 {
-       int error;
        struct task_struct *task = get_proc_task(dir);
        const struct pid_entry *p, *last;
-
-       error = -ENOENT;
+       struct dentry *res = ERR_PTR(-ENOENT);
 
        if (!task)
                goto out_no_task;
@@ -2489,11 +2469,11 @@ static struct dentry *proc_pident_lookup(struct inode *dir,
        if (p >= last)
                goto out;
 
-       error = proc_pident_instantiate(dir, dentry, task, p);
+       res = proc_pident_instantiate(dentry, task, p);
 out:
        put_task_struct(task);
 out_no_task:
-       return ERR_PTR(error);
+       return res;
 }
 
 static int proc_pident_readdir(struct file *file, struct dir_context *ctx,
@@ -3136,38 +3116,32 @@ void proc_flush_task(struct task_struct *task)
        }
 }
 
-static int proc_pid_instantiate(struct inode *dir,
-                                  struct dentry * dentry,
+static struct dentry *proc_pid_instantiate(struct dentry * dentry,
                                   struct task_struct *task, const void *ptr)
 {
        struct inode *inode;
 
-       inode = proc_pid_make_inode(dir->i_sb, task, S_IFDIR | S_IRUGO | S_IXUGO);
+       inode = proc_pid_make_inode(dentry->d_sb, task, S_IFDIR | S_IRUGO | S_IXUGO);
        if (!inode)
-               goto out;
+               return ERR_PTR(-ENOENT);
 
        inode->i_op = &proc_tgid_base_inode_operations;
        inode->i_fop = &proc_tgid_base_operations;
        inode->i_flags|=S_IMMUTABLE;
 
        set_nlink(inode, nlink_tgid);
+       pid_update_inode(task, inode);
 
        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, 0))
-               return 0;
-out:
-       return -ENOENT;
+       return d_splice_alias(inode, dentry);
 }
 
 struct dentry *proc_pid_lookup(struct inode *dir, struct dentry * dentry, unsigned int flags)
 {
-       int result = -ENOENT;
        struct task_struct *task;
        unsigned tgid;
        struct pid_namespace *ns;
+       struct dentry *result = ERR_PTR(-ENOENT);
 
        tgid = name_to_int(&dentry->d_name);
        if (tgid == ~0U)
@@ -3182,10 +3156,10 @@ struct dentry *proc_pid_lookup(struct inode *dir, struct dentry * dentry, unsign
        if (!task)
                goto out;
 
-       result = proc_pid_instantiate(dir, dentry, task, NULL);
+       result = proc_pid_instantiate(dentry, task, NULL);
        put_task_struct(task);
 out:
-       return ERR_PTR(result);
+       return result;
 }
 
 /*
@@ -3433,37 +3407,32 @@ static const struct inode_operations proc_tid_base_inode_operations = {
        .setattr        = proc_setattr,
 };
 
-static int proc_task_instantiate(struct inode *dir,
-       struct dentry *dentry, struct task_struct *task, const void *ptr)
+static struct dentry *proc_task_instantiate(struct dentry *dentry,
+       struct task_struct *task, const void *ptr)
 {
        struct inode *inode;
-       inode = proc_pid_make_inode(dir->i_sb, task, S_IFDIR | S_IRUGO | S_IXUGO);
-
+       inode = proc_pid_make_inode(dentry->d_sb, task, S_IFDIR | S_IRUGO | S_IXUGO);
        if (!inode)
-               goto out;
+               return ERR_PTR(-ENOENT);
+
        inode->i_op = &proc_tid_base_inode_operations;
        inode->i_fop = &proc_tid_base_operations;
-       inode->i_flags|=S_IMMUTABLE;
+       inode->i_flags |= S_IMMUTABLE;
 
        set_nlink(inode, nlink_tid);
+       pid_update_inode(task, inode);
 
        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, 0))
-               return 0;
-out:
-       return -ENOENT;
+       return d_splice_alias(inode, dentry);
 }
 
 static struct dentry *proc_task_lookup(struct inode *dir, struct dentry * dentry, unsigned int flags)
 {
-       int result = -ENOENT;
        struct task_struct *task;
        struct task_struct *leader = get_proc_task(dir);
        unsigned tid;
        struct pid_namespace *ns;
+       struct dentry *result = ERR_PTR(-ENOENT);
 
        if (!leader)
                goto out_no_task;
@@ -3483,13 +3452,13 @@ static struct dentry *proc_task_lookup(struct inode *dir, struct dentry * dentry
        if (!same_thread_group(leader, task))
                goto out_drop_task;
 
-       result = proc_task_instantiate(dir, dentry, task, NULL);
+       result = proc_task_instantiate(dentry, task, NULL);
 out_drop_task:
        put_task_struct(task);
 out:
        put_task_struct(leader);
 out_no_task:
-       return ERR_PTR(result);
+       return result;
 }
 
 /*