mirror of
				https://github.com/torvalds/linux.git
				synced 2025-11-04 02:30:34 +02:00 
			
		
		
		
	proc: fix lookup in /proc/net subdirectories after setns(2)
Commit1fde6f21d9("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>
This commit is contained in:
		
							parent
							
								
									fe71988834
								
							
						
					
					
						commit
						c6c75deda8
					
				
					 4 changed files with 36 additions and 19 deletions
				
			
		| 
						 | 
					@ -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,
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -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;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -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;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -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;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in a new issue