Subject: [PATCH 1/2] proc: revalidate directories created with proc_net_mkdir()

From: Alexey Dobriyan <[email-masked]>
Date: Sat, 6 Jul 2019 19:52:02 +0300

/proc/net directories may contain content which depends on net namespace.
Such dentries should be revalidated after setns(CLONE_NEWNET).

See
	commit 1fde6f21d90f8ba5da3cb9c54ca991ed72696c43
	proc: fix /proc/net/* after setns(2)

The patch is all about "pde_force_lookup()" line.

	[redid original patch --adobriyan]

Reported-by: "Hallsmark, Per" <[email-masked]>
Signed-off-by: Alexey Dobriyan <[email-masked]>
---

 fs/proc/generic.c       |   25 ++++++++++++++++++-------
 fs/proc/internal.h      |    3 +++
 fs/proc/proc_net.c      |   17 +++++++++++++++++
 include/linux/proc_fs.h |   16 ++++++++--------
 4 files changed, 46 insertions(+), 15 deletions(-)

--- a/fs/proc/generic.c
+++ b/fs/proc/generic.c
@@ -459,26 +459,37 @@ struct proc_dir_entry *proc_symlink(const char *name,
 }
 EXPORT_SYMBOL(proc_symlink);
 
-struct proc_dir_entry *proc_mkdir_data(const char *name, umode_t mode,
-		struct proc_dir_entry *parent, void *data)
+struct proc_dir_entry *_proc_mkdir(const char *name, umode_t mode,
+				   struct proc_dir_entry **parent, void *data)
 {
 	struct proc_dir_entry *ent;
 
 	if (mode == 0)
 		mode = S_IRUGO | S_IXUGO;
 
-	ent = __proc_create(&parent, name, S_IFDIR | mode, 2);
+	ent = __proc_create(parent, name, S_IFDIR | mode, 2);
 	if (ent) {
 		ent->data = data;
 		ent->proc_fops = &proc_dir_operations;
 		ent->proc_iops = &proc_dir_inode_operations;
-		parent->nlink++;
-		ent = proc_register(parent, ent);
-		if (!ent)
-			parent->nlink--;
 	}
 	return ent;
 }
+
+struct proc_dir_entry *proc_mkdir_data(const char *name, umode_t mode,
+		struct proc_dir_entry *parent, void *data)
+{
+	struct proc_dir_entry *pde;
+
+	pde = _proc_mkdir(name, mode, &parent, data);
+	if (!pde)
+		return NULL;
+	parent->nlink++;
+	pde = proc_register(parent, pde);
+	if (!pde)
+		parent->nlink--;
+	return pde;
+}
 EXPORT_SYMBOL_GPL(proc_mkdir_data);
 
 struct proc_dir_entry *proc_mkdir_mode(const char *name, umode_t mode,
--- a/fs/proc/internal.h
+++ b/fs/proc/internal.h
@@ -299,3 +299,6 @@ extern unsigned long task_statm(struct mm_struct *,
 				unsigned long *, unsigned long *,
 				unsigned long *, unsigned long *);
 extern void task_mem(struct seq_file *, struct mm_struct *);
+
+struct proc_dir_entry *_proc_mkdir(const char *name, umode_t mode,
+				   struct proc_dir_entry **parent, void *data);
--- a/fs/proc/proc_net.c
+++ b/fs/proc/proc_net.c
@@ -251,6 +251,23 @@ struct proc_dir_entry *proc_create_net_single_write(const char *name, umode_t mo
 }
 EXPORT_SYMBOL_GPL(proc_create_net_single_write);
 
+struct proc_dir_entry *proc_net_mkdir(struct net *net, const char *name,
+				      struct proc_dir_entry *parent)
+{
+	struct proc_dir_entry *pde;
+
+	pde = _proc_mkdir(name, 0, &parent, net);
+	if (!pde)
+		return NULL;
+	pde_force_lookup(pde);
+	parent->nlink++;
+	pde = proc_register(parent, pde);
+	if (!pde)
+		parent->nlink++;
+	return pde;
+}
+EXPORT_SYMBOL_GPL(proc_net_mkdir);
+
 static struct net *get_proc_task_net(struct inode *dir)
 {
 	struct task_struct *task;
--- a/include/linux/proc_fs.h
+++ b/include/linux/proc_fs.h
@@ -8,6 +8,7 @@
 #include <linux/types.h>
 #include <linux/fs.h>
 
+struct net;
 struct proc_dir_entry;
 struct seq_file;
 struct seq_operations;
@@ -73,6 +74,8 @@ struct proc_dir_entry *proc_create_net_single_write(const char *name, umode_t mo
 						    int (*show)(struct seq_file *, void *),
 						    proc_write_t write,
 						    void *data);
+struct proc_dir_entry *proc_net_mkdir(struct net *net, const char *name, struct proc_dir_entry *parent);
+
 extern struct pid *tgid_pidfd_to_pid(const struct file *file);
 
 #else /* CONFIG_PROC_FS */
@@ -115,6 +118,11 @@ static inline int remove_proc_subtree(const char *name, struct proc_dir_entry *p
 #define proc_create_net(name, mode, parent, state_size, ops) ({NULL;})
 #define proc_create_net_single(name, mode, parent, show, data) ({NULL;})
 
+static inline struct proc_dir_entry *proc_net_mkdir(struct net *net, const char *name, struct proc_dir_entry *parent)
+{
+	return NULL;
+}
+
 static inline struct pid *tgid_pidfd_to_pid(const struct file *file)
 {
 	return ERR_PTR(-EBADF);
@@ -122,14 +130,6 @@ static inline struct pid *tgid_pidfd_to_pid(const struct file *file)
 
 #endif /* CONFIG_PROC_FS */
 
-struct net;
-
-static inline struct proc_dir_entry *proc_net_mkdir(
-	struct net *net, const char *name, struct proc_dir_entry *parent)
-{
-	return proc_mkdir_data(name, 0, parent, net);
-}
-
 struct ns_common;
 int open_related_ns(struct ns_common *ns,
 		   struct ns_common *(*get_ns)(struct ns_common *ns));