proc: fix lookup in /proc/net subdirectories after setns(2)
[ 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: 1da4d377f9 ("proc: revalidate misc dentries")
Signed-off-by: Alexey Dobriyan <adobriyan@gmail.com>
Reported-by: "Rantala, Tommi T. (Nokia - FI/Espoo)" <tommi.t.rantala@nokia.com>
Cc: Al Viro <viro@zeniv.linux.org.uk>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Signed-off-by: Sasha Levin <sashal@kernel.org>
This commit is contained in:
committed by
Greg Kroah-Hartman
parent
972013f735
commit
6ccab11c56
@@ -341,6 +341,16 @@ static const struct file_operations proc_dir_operations = {
|
|||||||
.iterate_shared = proc_readdir,
|
.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..
|
* proc directories can do almost nothing..
|
||||||
*/
|
*/
|
||||||
@@ -463,8 +473,8 @@ struct proc_dir_entry *proc_symlink(const char *name,
|
|||||||
}
|
}
|
||||||
EXPORT_SYMBOL(proc_symlink);
|
EXPORT_SYMBOL(proc_symlink);
|
||||||
|
|
||||||
struct proc_dir_entry *proc_mkdir_data(const char *name, umode_t mode,
|
struct proc_dir_entry *_proc_mkdir(const char *name, umode_t mode,
|
||||||
struct proc_dir_entry *parent, void *data)
|
struct proc_dir_entry *parent, void *data, bool force_lookup)
|
||||||
{
|
{
|
||||||
struct proc_dir_entry *ent;
|
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->data = data;
|
||||||
ent->proc_fops = &proc_dir_operations;
|
ent->proc_fops = &proc_dir_operations;
|
||||||
ent->proc_iops = &proc_dir_inode_operations;
|
ent->proc_iops = &proc_dir_inode_operations;
|
||||||
|
if (force_lookup) {
|
||||||
|
pde_force_lookup(ent);
|
||||||
|
}
|
||||||
ent = proc_register(parent, ent);
|
ent = proc_register(parent, ent);
|
||||||
}
|
}
|
||||||
return 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);
|
EXPORT_SYMBOL_GPL(proc_mkdir_data);
|
||||||
|
|
||||||
struct proc_dir_entry *proc_mkdir_mode(const char *name, umode_t mode,
|
struct proc_dir_entry *proc_mkdir_mode(const char *name, umode_t mode,
|
||||||
|
|||||||
@@ -305,3 +305,10 @@ extern unsigned long task_statm(struct mm_struct *,
|
|||||||
unsigned long *, unsigned long *,
|
unsigned long *, unsigned long *,
|
||||||
unsigned long *, unsigned long *);
|
unsigned long *, unsigned long *);
|
||||||
extern void task_mem(struct seq_file *, struct mm_struct *);
|
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;
|
||||||
|
}
|
||||||
|
|||||||
@@ -38,22 +38,6 @@ static struct net *get_proc_net(const struct inode *inode)
|
|||||||
return maybe_get_net(PDE_NET(PDE(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)
|
static int seq_open_net(struct inode *inode, struct file *file)
|
||||||
{
|
{
|
||||||
unsigned int state_size = PDE(inode)->state_size;
|
unsigned int state_size = PDE(inode)->state_size;
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ extern void proc_flush_task(struct task_struct *);
|
|||||||
|
|
||||||
extern struct proc_dir_entry *proc_symlink(const char *,
|
extern struct proc_dir_entry *proc_symlink(const char *,
|
||||||
struct proc_dir_entry *, 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(const char *, struct proc_dir_entry *);
|
||||||
extern struct proc_dir_entry *proc_mkdir_data(const char *, umode_t,
|
extern struct proc_dir_entry *proc_mkdir_data(const char *, umode_t,
|
||||||
struct proc_dir_entry *, void *);
|
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,
|
static inline struct proc_dir_entry *proc_mkdir(const char *name,
|
||||||
struct proc_dir_entry *parent) {return NULL;}
|
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_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,
|
static inline struct proc_dir_entry *proc_mkdir_data(const char *name,
|
||||||
umode_t mode, struct proc_dir_entry *parent, void *data) { return NULL; }
|
umode_t mode, struct proc_dir_entry *parent, void *data) { return NULL; }
|
||||||
static inline struct proc_dir_entry *proc_mkdir_mode(const char *name,
|
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(
|
static inline struct proc_dir_entry *proc_net_mkdir(
|
||||||
struct net *net, const char *name, struct proc_dir_entry *parent)
|
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;
|
struct ns_common;
|
||||||
|
|||||||
Reference in New Issue
Block a user