Every entry that can be removed implements ->proc_read_buf() now and
procfs copies what is read to userspace after unpinning the entry.
->proc_read() and ->proc_read_iter() are left for the entries that are
never removed, kcore, kmsg, kpagecount, kpageflags, kpagecgroup and
profile. procfs never pins those, so nothing can wait for them.

Say so in struct proc_ops and refuse to register an entry that can be
removed and has one of them. The read paths then don't need to pin the
entry anymore.

PROC_ENTRY_PERMANENT is 0 in modules, so a module can't register an
entry with one of them anymore.

Signed-off-by: Christian Brauner (Amutable) <[email protected]>
---
 fs/proc/generic.c       |  4 ++++
 fs/proc/inode.c         | 27 ++++++++-------------------
 include/linux/proc_fs.h |  6 ++++++
 3 files changed, 18 insertions(+), 19 deletions(-)

diff --git a/fs/proc/generic.c b/fs/proc/generic.c
index 413382efefc7..7c8def4ed9fc 100644
--- a/fs/proc/generic.c
+++ b/fs/proc/generic.c
@@ -443,6 +443,10 @@ static int pde_set_flags(struct proc_dir_entry *pde)
 
        if (proc_ops->proc_flags & PROC_ENTRY_PERMANENT)
                pde->flags |= PROC_ENTRY_PERMANENT;
+       /* These copy to userspace and only an entry that can't go away may. */
+       if (WARN_ON_ONCE((proc_ops->proc_read || proc_ops->proc_read_iter) &&
+                        !pde_is_permanent(pde)))
+               return -EINVAL;
        if (proc_ops->proc_seq_ops) {
                pde->seq_ops = proc_ops->proc_seq_ops;
                pde->state_size = proc_ops->proc_state_size;
diff --git a/fs/proc/inode.c b/fs/proc/inode.c
index 971f2aecc006..a7f8818b0b74 100644
--- a/fs/proc/inode.c
+++ b/fs/proc/inode.c
@@ -460,35 +460,24 @@ static ssize_t pde_read_buf(struct proc_dir_entry *pde, 
struct kiocb *iocb,
 static ssize_t proc_reg_read_iter(struct kiocb *iocb, struct iov_iter *iter)
 {
        struct proc_dir_entry *pde = PDE(file_inode(iocb->ki_filp));
-       ssize_t ret;
 
        if (pde_has_proc_read_buf(pde))
                return pde_read_buf(pde, iocb, iter);
-       if (!pde_pin(pde))
-               return -EIO;
-       ret = pde->proc_ops->proc_read_iter(iocb, iter);
-       pde_unpin(pde);
-       return ret;
-}
-
-static ssize_t pde_read(struct proc_dir_entry *pde, struct file *file, char 
__user *buf, size_t count, loff_t *ppos)
-{
-       const auto read = pde->proc_ops->proc_read;
-       if (read)
-               return read(file, buf, count, ppos);
-       return -EIO;
+       /* only permanent entries have one, see pde_set_flags() */
+       return pde->proc_ops->proc_read_iter(iocb, iter);
 }
 
 static ssize_t proc_reg_read(struct file *file, char __user *buf, size_t 
count, loff_t *ppos)
 {
        struct proc_dir_entry *pde = PDE(file_inode(file));
-       ssize_t rv;
 
-       if (!pde_pin(pde))
+       /*
+        * Only permanent entries have one, see pde_set_flags(). Anything else
+        * might be gone along with its ->proc_ops.
+        */
+       if (!pde_is_permanent(pde) || !pde->proc_ops->proc_read)
                return -EIO;
-       rv = pde_read(pde, file, buf, count, ppos);
-       pde_unpin(pde);
-       return rv;
+       return pde->proc_ops->proc_read(file, buf, count, ppos);
 }
 
 /*
diff --git a/include/linux/proc_fs.h b/include/linux/proc_fs.h
index 64242a9aa117..17516b064922 100644
--- a/include/linux/proc_fs.h
+++ b/include/linux/proc_fs.h
@@ -17,6 +17,8 @@ struct seq_operations;
 enum {
        /*
         * All /proc entries using this ->proc_ops instance are never removed.
+        * procfs calls into them without pinning them, so they may copy to
+        * userspace themselves through ->proc_read() or ->proc_read_iter().
         *
         * If in doubt, ignore this flag.
         */
@@ -72,6 +74,10 @@ struct proc_ioctl_arg {
 struct proc_ops {
        unsigned int proc_flags;
        int     (*proc_open)(struct inode *, struct file *);
+       /*
+        * These two are only for PROC_ENTRY_PERMANENT entries. Everything else
+        * uses ->proc_read_buf().
+        */
        ssize_t (*proc_read)(struct file *, char __user *, size_t, loff_t *);
        ssize_t (*proc_read_iter)(struct kiocb *, struct iov_iter *);
        /*

-- 
2.53.0


Reply via email to