From: Alexey Dobriyan <adobri...@gmail.com>

[ Upstream commit c6c75deda81344c3a95d1d1f606d5cee109e5d54 ]

Commit 1fde6f21d90f ("proc: fix /proc/net/* after setns(2)") only forced
revalidation of regular files under /proc/net/

However, /proc/net/ is unusual in the sense of /proc/net/foo handlers
take netns pointer from parent directory which is old netns.

Steps to reproduce:

        (void)open("/proc/net/sctp/snmp", O_RDONLY);
        unshare(CLONE_NEWNET);

        int fd = open("/proc/net/sctp/snmp", O_RDONLY);
        read(fd, &c, 1);

Read will read wrong data from original netns.

Patch forces lookup on every directory under /proc/net .

Link: https://lkml.kernel.org/r/20201205160916.GA109739@localhost.localdomain
Fixes: 1da4d377f943 ("proc: revalidate misc dentries")
Signed-off-by: Alexey Dobriyan <adobri...@gmail.com>
Reported-by: "Rantala, Tommi T. (Nokia - FI/Espoo)" <tommi.t.rant...@nokia.com>
Cc: Al Viro <v...@zeniv.linux.org.uk>
Signed-off-by: Andrew Morton <a...@linux-foundation.org>
Signed-off-by: Linus Torvalds <torva...@linux-foundation.org>
Signed-off-by: Sasha Levin <sas...@kernel.org>
---
 fs/proc/generic.c       | 24 ++++++++++++++++++++++--
 fs/proc/internal.h      |  7 +++++++
 fs/proc/proc_net.c      | 16 ----------------
 include/linux/proc_fs.h |  8 +++++++-
 4 files changed, 36 insertions(+), 19 deletions(-)

diff --git a/fs/proc/generic.c b/fs/proc/generic.c
index 7820fe524cb03..bab10368a04d8 100644
--- a/fs/proc/generic.c
+++ b/fs/proc/generic.c
@@ -341,6 +341,16 @@ static const struct file_operations proc_dir_operations = {
        .iterate_shared         = proc_readdir,
 };
 
+static int proc_net_d_revalidate(struct dentry *dentry, unsigned int flags)
+{
+       return 0;
+}
+
+const struct dentry_operations proc_net_dentry_ops = {
+       .d_revalidate   = proc_net_d_revalidate,
+       .d_delete       = always_delete_dentry,
+};
+
 /*
  * proc directories can do almost nothing..
  */
@@ -463,8 +473,8 @@ struct proc_dir_entry *proc_symlink(const char *name,
 }
 EXPORT_SYMBOL(proc_symlink);
 
-struct proc_dir_entry *proc_mkdir_data(const char *name, umode_t mode,
-               struct proc_dir_entry *parent, void *data)
+struct proc_dir_entry *_proc_mkdir(const char *name, umode_t mode,
+               struct proc_dir_entry *parent, void *data, bool force_lookup)
 {
        struct proc_dir_entry *ent;
 
@@ -476,10 +486,20 @@ struct proc_dir_entry *proc_mkdir_data(const char *name, 
umode_t mode,
                ent->data = data;
                ent->proc_fops = &proc_dir_operations;
                ent->proc_iops = &proc_dir_inode_operations;
+               if (force_lookup) {
+                       pde_force_lookup(ent);
+               }
                ent = proc_register(parent, ent);
        }
        return ent;
 }
+EXPORT_SYMBOL_GPL(_proc_mkdir);
+
+struct proc_dir_entry *proc_mkdir_data(const char *name, umode_t mode,
+               struct proc_dir_entry *parent, void *data)
+{
+       return _proc_mkdir(name, mode, parent, data, false);
+}
 EXPORT_SYMBOL_GPL(proc_mkdir_data);
 
 struct proc_dir_entry *proc_mkdir_mode(const char *name, umode_t mode,
diff --git a/fs/proc/internal.h b/fs/proc/internal.h
index 95b14196f2842..4f14906ef16b5 100644
--- a/fs/proc/internal.h
+++ b/fs/proc/internal.h
@@ -305,3 +305,10 @@ extern unsigned long task_statm(struct mm_struct *,
                                unsigned long *, unsigned long *,
                                unsigned long *, unsigned long *);
 extern void task_mem(struct seq_file *, struct mm_struct *);
+
+extern const struct dentry_operations proc_net_dentry_ops;
+static inline void pde_force_lookup(struct proc_dir_entry *pde)
+{
+       /* /proc/net/ entries can be changed under us by setns(CLONE_NEWNET) */
+       pde->proc_dops = &proc_net_dentry_ops;
+}
diff --git a/fs/proc/proc_net.c b/fs/proc/proc_net.c
index a7b12435519e0..096bcc1e7a8a5 100644
--- a/fs/proc/proc_net.c
+++ b/fs/proc/proc_net.c
@@ -38,22 +38,6 @@ static struct net *get_proc_net(const struct inode *inode)
        return maybe_get_net(PDE_NET(PDE(inode)));
 }
 
-static int proc_net_d_revalidate(struct dentry *dentry, unsigned int flags)
-{
-       return 0;
-}
-
-static const struct dentry_operations proc_net_dentry_ops = {
-       .d_revalidate   = proc_net_d_revalidate,
-       .d_delete       = always_delete_dentry,
-};
-
-static void pde_force_lookup(struct proc_dir_entry *pde)
-{
-       /* /proc/net/ entries can be changed under us by setns(CLONE_NEWNET) */
-       pde->proc_dops = &proc_net_dentry_ops;
-}
-
 static int seq_open_net(struct inode *inode, struct file *file)
 {
        unsigned int state_size = PDE(inode)->state_size;
diff --git a/include/linux/proc_fs.h b/include/linux/proc_fs.h
index d0e1f1522a78e..5141657a0f7f6 100644
--- a/include/linux/proc_fs.h
+++ b/include/linux/proc_fs.h
@@ -21,6 +21,7 @@ extern void proc_flush_task(struct task_struct *);
 
 extern struct proc_dir_entry *proc_symlink(const char *,
                struct proc_dir_entry *, const char *);
+struct proc_dir_entry *_proc_mkdir(const char *, umode_t, struct 
proc_dir_entry *, void *, bool);
 extern struct proc_dir_entry *proc_mkdir(const char *, struct proc_dir_entry 
*);
 extern struct proc_dir_entry *proc_mkdir_data(const char *, umode_t,
                                              struct proc_dir_entry *, void *);
@@ -89,6 +90,11 @@ static inline struct proc_dir_entry *proc_symlink(const char 
*name,
 static inline struct proc_dir_entry *proc_mkdir(const char *name,
        struct proc_dir_entry *parent) {return NULL;}
 static inline struct proc_dir_entry *proc_create_mount_point(const char *name) 
{ return NULL; }
+static inline struct proc_dir_entry *_proc_mkdir(const char *name, umode_t 
mode,
+               struct proc_dir_entry *parent, void *data, bool force_lookup)
+{
+       return NULL;
+}
 static inline struct proc_dir_entry *proc_mkdir_data(const char *name,
        umode_t mode, struct proc_dir_entry *parent, void *data) { return NULL; 
}
 static inline struct proc_dir_entry *proc_mkdir_mode(const char *name,
@@ -121,7 +127,7 @@ struct net;
 static inline struct proc_dir_entry *proc_net_mkdir(
        struct net *net, const char *name, struct proc_dir_entry *parent)
 {
-       return proc_mkdir_data(name, 0, parent, net);
+       return _proc_mkdir(name, 0, parent, net, true);
 }
 
 struct ns_common;
-- 
2.27.0



Reply via email to