mirror of
				https://github.com/torvalds/linux.git
				synced 2025-11-04 02:30:34 +02:00 
			
		
		
		
	NFS: Fix /proc/fs/nfsfs/servers and /proc/fs/nfsfs/volumes
The usage of pid_ns->child_reaper->nsproxy->net_ns in nfs_server_list_open and nfs_client_list_open is not safe. /proc for a pid namespace can remain mounted after the all of the process in that pid namespace have exited. There are also times before the initial process in a pid namespace has started or after the initial process in a pid namespace has exited where pid_ns->child_reaper can be NULL or stale. Making the idiom pid_ns->child_reaper->nsproxy a double whammy of problems. Luckily all that needs to happen is to move /proc/fs/nfsfs/servers and /proc/fs/nfsfs/volumes under /proc/net to /proc/net/nfsfs/servers and /proc/net/nfsfs/volumes and add a symlink from the original location, and to use seq_open_net as it has been designed. Cc: stable@vger.kernel.org Cc: Trond Myklebust <trond.myklebust@primarydata.com> Cc: Stanislav Kinsbursky <skinsbursky@parallels.com> Signed-off-by: "Eric W. Biederman" <ebiederm@xmission.com>
This commit is contained in:
		
							parent
							
								
									db181ce011
								
							
						
					
					
						commit
						65b38851a1
					
				
					 4 changed files with 69 additions and 41 deletions
				
			
		| 
						 | 
					@ -1205,7 +1205,7 @@ static const struct file_operations nfs_server_list_fops = {
 | 
				
			||||||
	.open		= nfs_server_list_open,
 | 
						.open		= nfs_server_list_open,
 | 
				
			||||||
	.read		= seq_read,
 | 
						.read		= seq_read,
 | 
				
			||||||
	.llseek		= seq_lseek,
 | 
						.llseek		= seq_lseek,
 | 
				
			||||||
	.release	= seq_release,
 | 
						.release	= seq_release_net,
 | 
				
			||||||
	.owner		= THIS_MODULE,
 | 
						.owner		= THIS_MODULE,
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1226,7 +1226,7 @@ static const struct file_operations nfs_volume_list_fops = {
 | 
				
			||||||
	.open		= nfs_volume_list_open,
 | 
						.open		= nfs_volume_list_open,
 | 
				
			||||||
	.read		= seq_read,
 | 
						.read		= seq_read,
 | 
				
			||||||
	.llseek		= seq_lseek,
 | 
						.llseek		= seq_lseek,
 | 
				
			||||||
	.release	= seq_release,
 | 
						.release	= seq_release_net,
 | 
				
			||||||
	.owner		= THIS_MODULE,
 | 
						.owner		= THIS_MODULE,
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1236,19 +1236,8 @@ static const struct file_operations nfs_volume_list_fops = {
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
static int nfs_server_list_open(struct inode *inode, struct file *file)
 | 
					static int nfs_server_list_open(struct inode *inode, struct file *file)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct seq_file *m;
 | 
						return seq_open_net(inode, file, &nfs_server_list_ops,
 | 
				
			||||||
	int ret;
 | 
								   sizeof(struct seq_net_private));
 | 
				
			||||||
	struct pid_namespace *pid_ns = file->f_dentry->d_sb->s_fs_info;
 | 
					 | 
				
			||||||
	struct net *net = pid_ns->child_reaper->nsproxy->net_ns;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	ret = seq_open(file, &nfs_server_list_ops);
 | 
					 | 
				
			||||||
	if (ret < 0)
 | 
					 | 
				
			||||||
		return ret;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	m = file->private_data;
 | 
					 | 
				
			||||||
	m->private = net;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return 0;
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/*
 | 
					/*
 | 
				
			||||||
| 
						 | 
					@ -1256,7 +1245,7 @@ static int nfs_server_list_open(struct inode *inode, struct file *file)
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
static void *nfs_server_list_start(struct seq_file *m, loff_t *_pos)
 | 
					static void *nfs_server_list_start(struct seq_file *m, loff_t *_pos)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct nfs_net *nn = net_generic(m->private, nfs_net_id);
 | 
						struct nfs_net *nn = net_generic(seq_file_net(m), nfs_net_id);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* lock the list against modification */
 | 
						/* lock the list against modification */
 | 
				
			||||||
	spin_lock(&nn->nfs_client_lock);
 | 
						spin_lock(&nn->nfs_client_lock);
 | 
				
			||||||
| 
						 | 
					@ -1268,7 +1257,7 @@ static void *nfs_server_list_start(struct seq_file *m, loff_t *_pos)
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
static void *nfs_server_list_next(struct seq_file *p, void *v, loff_t *pos)
 | 
					static void *nfs_server_list_next(struct seq_file *p, void *v, loff_t *pos)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct nfs_net *nn = net_generic(p->private, nfs_net_id);
 | 
						struct nfs_net *nn = net_generic(seq_file_net(p), nfs_net_id);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return seq_list_next(v, &nn->nfs_client_list, pos);
 | 
						return seq_list_next(v, &nn->nfs_client_list, pos);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -1278,7 +1267,7 @@ static void *nfs_server_list_next(struct seq_file *p, void *v, loff_t *pos)
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
static void nfs_server_list_stop(struct seq_file *p, void *v)
 | 
					static void nfs_server_list_stop(struct seq_file *p, void *v)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct nfs_net *nn = net_generic(p->private, nfs_net_id);
 | 
						struct nfs_net *nn = net_generic(seq_file_net(p), nfs_net_id);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	spin_unlock(&nn->nfs_client_lock);
 | 
						spin_unlock(&nn->nfs_client_lock);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -1289,7 +1278,7 @@ static void nfs_server_list_stop(struct seq_file *p, void *v)
 | 
				
			||||||
static int nfs_server_list_show(struct seq_file *m, void *v)
 | 
					static int nfs_server_list_show(struct seq_file *m, void *v)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct nfs_client *clp;
 | 
						struct nfs_client *clp;
 | 
				
			||||||
	struct nfs_net *nn = net_generic(m->private, nfs_net_id);
 | 
						struct nfs_net *nn = net_generic(seq_file_net(m), nfs_net_id);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* display header on line 1 */
 | 
						/* display header on line 1 */
 | 
				
			||||||
	if (v == &nn->nfs_client_list) {
 | 
						if (v == &nn->nfs_client_list) {
 | 
				
			||||||
| 
						 | 
					@ -1321,19 +1310,8 @@ static int nfs_server_list_show(struct seq_file *m, void *v)
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
static int nfs_volume_list_open(struct inode *inode, struct file *file)
 | 
					static int nfs_volume_list_open(struct inode *inode, struct file *file)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct seq_file *m;
 | 
						return seq_open_net(inode, file, &nfs_server_list_ops,
 | 
				
			||||||
	int ret;
 | 
								   sizeof(struct seq_net_private));
 | 
				
			||||||
	struct pid_namespace *pid_ns = file->f_dentry->d_sb->s_fs_info;
 | 
					 | 
				
			||||||
	struct net *net = pid_ns->child_reaper->nsproxy->net_ns;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	ret = seq_open(file, &nfs_volume_list_ops);
 | 
					 | 
				
			||||||
	if (ret < 0)
 | 
					 | 
				
			||||||
		return ret;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	m = file->private_data;
 | 
					 | 
				
			||||||
	m->private = net;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return 0;
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/*
 | 
					/*
 | 
				
			||||||
| 
						 | 
					@ -1341,7 +1319,7 @@ static int nfs_volume_list_open(struct inode *inode, struct file *file)
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
static void *nfs_volume_list_start(struct seq_file *m, loff_t *_pos)
 | 
					static void *nfs_volume_list_start(struct seq_file *m, loff_t *_pos)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct nfs_net *nn = net_generic(m->private, nfs_net_id);
 | 
						struct nfs_net *nn = net_generic(seq_file_net(m), nfs_net_id);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* lock the list against modification */
 | 
						/* lock the list against modification */
 | 
				
			||||||
	spin_lock(&nn->nfs_client_lock);
 | 
						spin_lock(&nn->nfs_client_lock);
 | 
				
			||||||
| 
						 | 
					@ -1353,7 +1331,7 @@ static void *nfs_volume_list_start(struct seq_file *m, loff_t *_pos)
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
static void *nfs_volume_list_next(struct seq_file *p, void *v, loff_t *pos)
 | 
					static void *nfs_volume_list_next(struct seq_file *p, void *v, loff_t *pos)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct nfs_net *nn = net_generic(p->private, nfs_net_id);
 | 
						struct nfs_net *nn = net_generic(seq_file_net(p), nfs_net_id);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return seq_list_next(v, &nn->nfs_volume_list, pos);
 | 
						return seq_list_next(v, &nn->nfs_volume_list, pos);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -1363,7 +1341,7 @@ static void *nfs_volume_list_next(struct seq_file *p, void *v, loff_t *pos)
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
static void nfs_volume_list_stop(struct seq_file *p, void *v)
 | 
					static void nfs_volume_list_stop(struct seq_file *p, void *v)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct nfs_net *nn = net_generic(p->private, nfs_net_id);
 | 
						struct nfs_net *nn = net_generic(seq_file_net(p), nfs_net_id);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	spin_unlock(&nn->nfs_client_lock);
 | 
						spin_unlock(&nn->nfs_client_lock);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -1376,7 +1354,7 @@ static int nfs_volume_list_show(struct seq_file *m, void *v)
 | 
				
			||||||
	struct nfs_server *server;
 | 
						struct nfs_server *server;
 | 
				
			||||||
	struct nfs_client *clp;
 | 
						struct nfs_client *clp;
 | 
				
			||||||
	char dev[8], fsid[17];
 | 
						char dev[8], fsid[17];
 | 
				
			||||||
	struct nfs_net *nn = net_generic(m->private, nfs_net_id);
 | 
						struct nfs_net *nn = net_generic(seq_file_net(m), nfs_net_id);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* display header on line 1 */
 | 
						/* display header on line 1 */
 | 
				
			||||||
	if (v == &nn->nfs_volume_list) {
 | 
						if (v == &nn->nfs_volume_list) {
 | 
				
			||||||
| 
						 | 
					@ -1407,6 +1385,45 @@ static int nfs_volume_list_show(struct seq_file *m, void *v)
 | 
				
			||||||
	return 0;
 | 
						return 0;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int nfs_fs_proc_net_init(struct net *net)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct nfs_net *nn = net_generic(net, nfs_net_id);
 | 
				
			||||||
 | 
						struct proc_dir_entry *p;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						nn->proc_nfsfs = proc_net_mkdir(net, "nfsfs", net->proc_net);
 | 
				
			||||||
 | 
						if (!nn->proc_nfsfs)
 | 
				
			||||||
 | 
							goto error_0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* a file of servers with which we're dealing */
 | 
				
			||||||
 | 
						p = proc_create("servers", S_IFREG|S_IRUGO,
 | 
				
			||||||
 | 
								nn->proc_nfsfs, &nfs_server_list_fops);
 | 
				
			||||||
 | 
						if (!p)
 | 
				
			||||||
 | 
							goto error_1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* a file of volumes that we have mounted */
 | 
				
			||||||
 | 
						p = proc_create("volumes", S_IFREG|S_IRUGO,
 | 
				
			||||||
 | 
								nn->proc_nfsfs, &nfs_volume_list_fops);
 | 
				
			||||||
 | 
						if (!p)
 | 
				
			||||||
 | 
							goto error_2;
 | 
				
			||||||
 | 
						return 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					error_2:
 | 
				
			||||||
 | 
						remove_proc_entry("servers", nn->proc_nfsfs);
 | 
				
			||||||
 | 
					error_1:
 | 
				
			||||||
 | 
						remove_proc_entry("fs/nfsfs", NULL);
 | 
				
			||||||
 | 
					error_0:
 | 
				
			||||||
 | 
						return -ENOMEM;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void nfs_fs_proc_net_exit(struct net *net)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct nfs_net *nn = net_generic(net, nfs_net_id);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						remove_proc_entry("volumes", nn->proc_nfsfs);
 | 
				
			||||||
 | 
						remove_proc_entry("servers", nn->proc_nfsfs);
 | 
				
			||||||
 | 
						remove_proc_entry("fs/nfsfs", NULL);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/*
 | 
					/*
 | 
				
			||||||
 * initialise the /proc/fs/nfsfs/ directory
 | 
					 * initialise the /proc/fs/nfsfs/ directory
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
| 
						 | 
					@ -1419,14 +1436,12 @@ int __init nfs_fs_proc_init(void)
 | 
				
			||||||
		goto error_0;
 | 
							goto error_0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* a file of servers with which we're dealing */
 | 
						/* a file of servers with which we're dealing */
 | 
				
			||||||
	p = proc_create("servers", S_IFREG|S_IRUGO,
 | 
						p = proc_symlink("servers", proc_fs_nfs, "../../net/nfsfs/servers");
 | 
				
			||||||
			proc_fs_nfs, &nfs_server_list_fops);
 | 
					 | 
				
			||||||
	if (!p)
 | 
						if (!p)
 | 
				
			||||||
		goto error_1;
 | 
							goto error_1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* a file of volumes that we have mounted */
 | 
						/* a file of volumes that we have mounted */
 | 
				
			||||||
	p = proc_create("volumes", S_IFREG|S_IRUGO,
 | 
						p = proc_symlink("volumes", proc_fs_nfs, "../../net/nfsfs/volumes");
 | 
				
			||||||
			proc_fs_nfs, &nfs_volume_list_fops);
 | 
					 | 
				
			||||||
	if (!p)
 | 
						if (!p)
 | 
				
			||||||
		goto error_2;
 | 
							goto error_2;
 | 
				
			||||||
	return 0;
 | 
						return 0;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1840,11 +1840,12 @@ EXPORT_SYMBOL_GPL(nfs_net_id);
 | 
				
			||||||
static int nfs_net_init(struct net *net)
 | 
					static int nfs_net_init(struct net *net)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	nfs_clients_init(net);
 | 
						nfs_clients_init(net);
 | 
				
			||||||
	return 0;
 | 
						return nfs_fs_proc_net_init(net);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void nfs_net_exit(struct net *net)
 | 
					static void nfs_net_exit(struct net *net)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
 | 
						nfs_fs_proc_net_exit(net);
 | 
				
			||||||
	nfs_cleanup_cb_ident_idr(net);
 | 
						nfs_cleanup_cb_ident_idr(net);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -195,7 +195,16 @@ extern struct rpc_clnt *nfs4_find_or_create_ds_client(struct nfs_client *,
 | 
				
			||||||
#ifdef CONFIG_PROC_FS
 | 
					#ifdef CONFIG_PROC_FS
 | 
				
			||||||
extern int __init nfs_fs_proc_init(void);
 | 
					extern int __init nfs_fs_proc_init(void);
 | 
				
			||||||
extern void nfs_fs_proc_exit(void);
 | 
					extern void nfs_fs_proc_exit(void);
 | 
				
			||||||
 | 
					extern int nfs_fs_proc_net_init(struct net *net);
 | 
				
			||||||
 | 
					extern void nfs_fs_proc_net_exit(struct net *net);
 | 
				
			||||||
#else
 | 
					#else
 | 
				
			||||||
 | 
					static inline int nfs_fs_proc_net_init(struct net *net)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					static inline void nfs_fs_proc_net_exit(struct net *net)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
static inline int nfs_fs_proc_init(void)
 | 
					static inline int nfs_fs_proc_init(void)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	return 0;
 | 
						return 0;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -29,6 +29,9 @@ struct nfs_net {
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
	spinlock_t nfs_client_lock;
 | 
						spinlock_t nfs_client_lock;
 | 
				
			||||||
	struct timespec boot_time;
 | 
						struct timespec boot_time;
 | 
				
			||||||
 | 
					#ifdef CONFIG_PROC_FS
 | 
				
			||||||
 | 
						struct proc_dir_entry *proc_nfsfs;
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
extern int nfs_net_id;
 | 
					extern int nfs_net_id;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in a new issue