Commit c6c75ded authored by Alexey Dobriyan's avatar Alexey Dobriyan Committed by Linus Torvalds
Browse files

proc: fix lookup in /proc/net subdirectories after setns(2)

Commit 1fde6f21 ("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: 1da4d377 ("proc: revalidate misc dentries")
Signed-off-by: default avatarAlexey Dobriyan <adobriyan@gmail.com>
Reported-by: default avatar"Rantala, Tommi T. (Nokia - FI/Espoo)" <tommi.t.rantala@nokia.com>
Cc: Al Viro <viro@zeniv.linux.org.uk>
Signed-off-by: default avatarAndrew Morton <akpm@linux-foundation.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@linux-foundation.org>
parent fe719888
Loading
Loading
Loading
Loading
+22 −2
Original line number Original line Diff line number Diff line
@@ -349,6 +349,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..
 */
 */
@@ -471,8 +481,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;


@@ -484,10 +494,20 @@ struct proc_dir_entry *proc_mkdir_data(const char *name, umode_t mode,
		ent->data = data;
		ent->data = data;
		ent->proc_dir_ops = &proc_dir_operations;
		ent->proc_dir_ops = &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,
+7 −0
Original line number Original line Diff line number Diff line
@@ -310,3 +310,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;
}
+0 −16
Original line number Original line Diff line number Diff line
@@ -39,22 +39,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;
+7 −1
Original line number Original line Diff line number Diff line
@@ -80,6 +80,7 @@ extern void proc_flush_pid(struct pid *);


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 *);
@@ -162,6 +163,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,
@@ -199,7 +205,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;