/proc entries under /proc/net/* can't be cached into dcache
because setns(2) can change current net namespace.

Reported-by: Mateusz Stępień <[email protected]>
Reported-by: Ahmad Fatoum <[email protected]>
Fixes: 1da4d377f943fe4194ffb9fb9c26cc58fad4dd24 ("proc: revalidate misc 
dentries")
Signed-off-by: Alexey Dobriyan <[email protected]>
---

        I'm still looking into it because modules can create entries
        under /proc/net/* :-(

 fs/proc/generic.c  |    4 +++-
 fs/proc/internal.h |    1 +
 fs/proc/proc_net.c |   10 ++++++++++
 3 files changed, 14 insertions(+), 1 deletion(-)

--- a/fs/proc/generic.c
+++ b/fs/proc/generic.c
@@ -256,7 +256,7 @@ struct dentry *proc_lookup_de(struct inode *dir, struct 
dentry *dentry,
                inode = proc_get_inode(dir->i_sb, de);
                if (!inode)
                        return ERR_PTR(-ENOMEM);
-               d_set_d_op(dentry, &proc_misc_dentry_ops);
+               d_set_d_op(dentry, de->proc_dops);
                return d_splice_alias(inode, dentry);
        }
        read_unlock(&proc_subdir_lock);
@@ -429,6 +429,8 @@ static struct proc_dir_entry *__proc_create(struct 
proc_dir_entry **parent,
        INIT_LIST_HEAD(&ent->pde_openers);
        proc_set_user(ent, (*parent)->uid, (*parent)->gid);
 
+       ent->proc_dops = &proc_misc_dentry_ops;
+
 out:
        return ent;
 }
--- a/fs/proc/internal.h
+++ b/fs/proc/internal.h
@@ -44,6 +44,7 @@ struct proc_dir_entry {
        struct completion *pde_unload_completion;
        const struct inode_operations *proc_iops;
        const struct file_operations *proc_fops;
+       const struct dentry_operations *proc_dops;
        union {
                const struct seq_operations *seq_ops;
                int (*single_show)(struct seq_file *, void *);
--- a/fs/proc/proc_net.c
+++ b/fs/proc/proc_net.c
@@ -38,6 +38,12 @@ static struct net *get_proc_net(const struct inode *inode)
        return maybe_get_net(PDE_NET(PDE(inode)));
 }
 
+static void pde_force_lookup(struct proc_dir_entry *pde)
+{
+       // /proc/net/* can be changed under us by setns(CLONE_NEWNET)
+       pde->proc_dops = &simple_dentry_operations;
+}
+
 static int seq_open_net(struct inode *inode, struct file *file)
 {
        unsigned int state_size = PDE(inode)->state_size;
@@ -90,6 +96,7 @@ struct proc_dir_entry *proc_create_net_data(const char *name, 
umode_t mode,
        p = proc_create_reg(name, mode, &parent, data);
        if (!p)
                return NULL;
+       pde_force_lookup(p);
        p->proc_fops = &proc_net_seq_fops;
        p->seq_ops = ops;
        p->state_size = state_size;
@@ -133,6 +140,7 @@ struct proc_dir_entry *proc_create_net_data_write(const 
char *name, umode_t mode
        p = proc_create_reg(name, mode, &parent, data);
        if (!p)
                return NULL;
+       pde_force_lookup(p);
        p->proc_fops = &proc_net_seq_fops;
        p->seq_ops = ops;
        p->state_size = state_size;
@@ -181,6 +189,7 @@ struct proc_dir_entry *proc_create_net_single(const char 
*name, umode_t mode,
        p = proc_create_reg(name, mode, &parent, data);
        if (!p)
                return NULL;
+       pde_force_lookup(p);
        p->proc_fops = &proc_net_single_fops;
        p->single_show = show;
        return proc_register(parent, p);
@@ -223,6 +232,7 @@ struct proc_dir_entry *proc_create_net_single_write(const 
char *name, umode_t mo
        p = proc_create_reg(name, mode, &parent, data);
        if (!p)
                return NULL;
+       pde_force_lookup(p);
        p->proc_fops = &proc_net_single_fops;
        p->single_show = show;
        p->write = write;

Reply via email to