linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* /proc/net/sctp/snmp, setns, proc: revalidate misc dentries
@ 2020-12-02  6:18 Rantala, Tommi T. (Nokia - FI/Espoo)
  2020-12-05 16:09 ` [PATCH] proc: fix lookup in /proc/net subdirectories after setns(2) Alexey Dobriyan
  0 siblings, 1 reply; 3+ messages in thread
From: Rantala, Tommi T. (Nokia - FI/Espoo) @ 2020-12-02  6:18 UTC (permalink / raw)
  To: adobriyan; +Cc: linux-fsdevel, linux-kernel, akpm

[-- Attachment #1: Type: text/plain, Size: 3860 bytes --]

Hello,

Bisected problems with setns() and /proc/net/sctp/snmp to this:

commit 1da4d377f943fe4194ffb9fb9c26cc58fad4dd24
Author: Alexey Dobriyan <adobriyan@gmail.com>
Date:   Fri Apr 13 15:35:42 2018 -0700

    proc: revalidate misc dentries

Reproduces for example with Fedora 5.9.10-100.fc32.x86_64, so 1fde6f21d90f
("proc: fix /proc/net/* after setns(2)") does not seem to cover
/proc/net/sctp/snmp


Reproducer attached, that does open+read+close of /proc/net/sctp/snmp before
and after setns() syscall. The second open+read+close of /proc/net/sctp/snmp
incorrectly produces results for the default namespace, not the target
namespace.


Example, create netns and do some sctp:

# ./iperf-netns
+ modprobe sctp
+ ip netns add test
+ ip netns exec test ip link set lo up
+ ip netns exec test iperf3 -s -1
-----------------------------------------------------------
Server listening on 5201
-----------------------------------------------------------
+ ip netns exec test iperf3 -c 127.0.0.1 --sctp --bitrate 50M --time 4
Connecting to host 127.0.0.1, port 5201
Accepted connection from 127.0.0.1, port 50696
[  5] local 127.0.0.1 port 54735 connected to 127.0.0.1 port 5201
[  5] local 127.0.0.1 port 5201 connected to 127.0.0.1 port 54735
[ ID] Interval           Transfer     Bitrate
[ ID] Interval           Transfer     Bitrate
[  5]   0.00-1.00   sec  6.00 MBytes  50.3 Mbits/sec                  
[  5]   0.00-1.00   sec  6.00 MBytes  50.3 Mbits/sec                  
[  5]   1.00-2.00   sec  5.94 MBytes  49.8 Mbits/sec                  
[  5]   1.00-2.00   sec  5.94 MBytes  49.8 Mbits/sec                  
[  5]   2.00-3.00   sec  6.00 MBytes  50.3 Mbits/sec                  
[  5]   2.00-3.00   sec  6.00 MBytes  50.3 Mbits/sec                  
[  5]   3.00-4.00   sec  5.94 MBytes  49.8 Mbits/sec                  
- - - - - - - - - - - - - - - - - - - - - - - - -
[ ID] Interval           Transfer     Bitrate
[  5]   0.00-4.00   sec  23.9 MBytes  50.1
Mbits/sec                  receiver
[  5]   3.00-4.00   sec  5.94 MBytes  49.8 Mbits/sec                  
- - - - - - - - - - - - - - - - - - - - - - - - -
[ ID] Interval           Transfer     Bitrate
[  5]   0.00-4.00   sec  23.9 MBytes  50.1 Mbits/sec
[  5]   0.00-4.00   sec  23.9 MBytes  50.1

iperf Done.
+ cat /proc/net/sctp/snmp
SctpCurrEstab                           0
SctpActiveEstabs                        0
SctpPassiveEstabs                       0
SctpAborteds                            0
SctpShutdowns                           0
SctpOutOfBlues                          0
SctpChecksumErrors                      0
[...]
+ ip netns exec test cat /proc/net/sctp/snmp
SctpCurrEstab                           0
SctpActiveEstabs                        2
SctpPassiveEstabs                       2
SctpAborteds                            0
SctpShutdowns                           4
SctpOutOfBlues                          0
SctpChecksumErrors                      0
SctpOutCtrlChunks                       1544
SctpOutOrderChunks                      1530
[...]
+ wait


But now we see all zeroes in /proc/net/sctp/snmp with the reproducer:

$ gcc repro.c -o repro      
             
# ./repro
/proc/net/sctp/snmp [pid: 175998]
SctpCurrEstab                           0
SctpActiveEstabs                        0
SctpPassiveEstabs                       0
SctpAborteds                            0
SctpShutdowns                           0
[...]

setns(/run/netns/test) ...
/proc/net/sctp/snmp [pid: 175998]
SctpCurrEstab                           0
SctpActiveEstabs                        0
SctpPassiveEstabs                       0
SctpAborteds                            0
SctpShutdowns                           0
SctpOutOfBlues                          0
[...]


-Tommi

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: repro.c --]
[-- Type: text/x-csrc; name="repro.c", Size: 1127 bytes --]

#define _GNU_SOURCE
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <sched.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

void slurp(const char *fn)
{
	char buf[8192];
	ssize_t r;
	int fd;

	printf("%s [pid: %d]\n", fn, getpid()); fflush(stdout);

	fd = open(fn, O_RDONLY);
	if (fd < 0) { perror("open"); exit(1); }

	r = read(fd, buf, sizeof(buf)-1);
	if (r < 0) { perror("read"); exit(1); }
	buf[r] = 0;
	puts(buf); fflush(stdout);

	if (close(fd) < 0) { perror("close"); exit(1); }
}

void newnet(const char *ns)
{
	int fd;
	fd = open(ns, O_RDONLY);
	if (fd < 0) { perror("open"); exit(1); }
	if (setns(fd, CLONE_NEWNET) < 0) { perror("setns"); exit(1); }
	if (close(fd) < 0) { perror("close"); exit(1); }
}

int main(int argc, char **argv)
{
	const char *ns = "/run/netns/test";
	const char *fn = "/proc/net/sctp/snmp";
	int d = 1;

	// Optional args: /run/netns/... /proc/net/... n
	if (argc >= 2) ns = argv[1];
	if (argc >= 3) fn = argv[2];
	if (argc >= 4 && argv[3][0] == 'n') d = 0;

	if (d) slurp(fn);
	printf("setns(%s) ...\n", ns); fflush(stdout);
	newnet(ns);
	slurp(fn);
}

[-- Attachment #3: iperf-netns --]
[-- Type: application/x-shellscript, Size: 303 bytes --]

^ permalink raw reply	[flat|nested] 3+ messages in thread

* [PATCH] proc: fix lookup in /proc/net subdirectories after setns(2)
  2020-12-02  6:18 /proc/net/sctp/snmp, setns, proc: revalidate misc dentries Rantala, Tommi T. (Nokia - FI/Espoo)
@ 2020-12-05 16:09 ` Alexey Dobriyan
  2020-12-05 18:50   ` Alexey Dobriyan
  0 siblings, 1 reply; 3+ messages in thread
From: Alexey Dobriyan @ 2020-12-05 16:09 UTC (permalink / raw)
  To: akpm; +Cc: linux-fsdevel, linux-kernel, tommi.t.rantala

	commit 1fde6f21d90f8ba5da3cb9c54ca991ed72696c43
	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 .

Fixes: 1da4d377f943 ("proc: revalidate misc dentries")
Reported-by: "Rantala, Tommi T. (Nokia - FI/Espoo)" <tommi.t.rantala@nokia.com>
Signed-off-by: Alexey Dobriyan <adobriyan@gmail.com>
---

 fs/proc/generic.c       |   24 ++++++++++++++++++++++--
 fs/proc/internal.h      |    7 +++++++
 fs/proc/proc_net.c      |   16 ----------------
 include/linux/proc_fs.h |    8 +++++++-
 4 files changed, 36 insertions(+), 19 deletions(-)

--- a/fs/proc/generic.c
+++ b/fs/proc/generic.c
@@ -349,6 +349,16 @@ static const struct file_operations proc_dir_operations = {
 	.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..
  */
@@ -471,8 +481,8 @@ 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, bool force_lookup)
 {
 	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->proc_dir_ops = &proc_dir_operations;
 		ent->proc_iops = &proc_dir_inode_operations;
+		if (force_lookup) {
+			pde_force_lookup(ent);
+		}
 		ent = proc_register(parent, 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);
 
 struct proc_dir_entry *proc_mkdir_mode(const char *name, umode_t mode,
--- a/fs/proc/internal.h
+++ b/fs/proc/internal.h
@@ -310,3 +310,10 @@ 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 *);
+
+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;
+}
--- a/fs/proc/proc_net.c
+++ b/fs/proc/proc_net.c
@@ -39,22 +39,6 @@ static struct net *get_proc_net(const struct inode *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)
 {
 	unsigned int state_size = PDE(inode)->state_size;
--- a/include/linux/proc_fs.h
+++ b/include/linux/proc_fs.h
@@ -80,6 +80,7 @@ extern void proc_flush_pid(struct pid *);
 
 extern struct proc_dir_entry *proc_symlink(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_data(const char *, umode_t,
 					      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,
 	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_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,
 	umode_t mode, struct proc_dir_entry *parent, void *data) { return NULL; }
 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(
 	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;

^ permalink raw reply	[flat|nested] 3+ messages in thread

* [PATCH] proc: fix lookup in /proc/net subdirectories after setns(2)
  2020-12-05 16:09 ` [PATCH] proc: fix lookup in /proc/net subdirectories after setns(2) Alexey Dobriyan
@ 2020-12-05 18:50   ` Alexey Dobriyan
  0 siblings, 0 replies; 3+ messages in thread
From: Alexey Dobriyan @ 2020-12-05 18:50 UTC (permalink / raw)
  To: akpm; +Cc: linux-fsdevel, linux-kernel, tommi.t.rantala

	commit 1fde6f21d90f8ba5da3cb9c54ca991ed72696c43
	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 .

Fixes: 1da4d377f943 ("proc: revalidate misc dentries")
Reported-by: "Rantala, Tommi T. (Nokia - FI/Espoo)" <tommi.t.rantala@nokia.com>
Signed-off-by: Alexey Dobriyan <adobriyan@gmail.com>
---

 fs/proc/generic.c       |   24 ++++++++++++++++++++++--
 fs/proc/internal.h      |    7 +++++++
 fs/proc/proc_net.c      |   16 ----------------
 include/linux/proc_fs.h |    8 +++++++-
 4 files changed, 36 insertions(+), 19 deletions(-)

--- a/fs/proc/generic.c
+++ b/fs/proc/generic.c
@@ -349,6 +349,16 @@ static const struct file_operations proc_dir_operations = {
 	.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..
  */
@@ -471,8 +481,8 @@ 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, bool force_lookup)
 {
 	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->proc_dir_ops = &proc_dir_operations;
 		ent->proc_iops = &proc_dir_inode_operations;
+		if (force_lookup) {
+			pde_force_lookup(ent);
+		}
 		ent = proc_register(parent, 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);
 
 struct proc_dir_entry *proc_mkdir_mode(const char *name, umode_t mode,
--- a/fs/proc/internal.h
+++ b/fs/proc/internal.h
@@ -310,3 +310,10 @@ 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 *);
+
+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;
+}
--- a/fs/proc/proc_net.c
+++ b/fs/proc/proc_net.c
@@ -39,22 +39,6 @@ static struct net *get_proc_net(const struct inode *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)
 {
 	unsigned int state_size = PDE(inode)->state_size;
--- a/include/linux/proc_fs.h
+++ b/include/linux/proc_fs.h
@@ -80,6 +80,7 @@ extern void proc_flush_pid(struct pid *);
 
 extern struct proc_dir_entry *proc_symlink(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_data(const char *, umode_t,
 					      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,
 	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_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,
 	umode_t mode, struct proc_dir_entry *parent, void *data) { return NULL; }
 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(
 	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;

^ permalink raw reply	[flat|nested] 3+ messages in thread

end of thread, other threads:[~2020-12-05 18:51 UTC | newest]

Thread overview: 3+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-12-02  6:18 /proc/net/sctp/snmp, setns, proc: revalidate misc dentries Rantala, Tommi T. (Nokia - FI/Espoo)
2020-12-05 16:09 ` [PATCH] proc: fix lookup in /proc/net subdirectories after setns(2) Alexey Dobriyan
2020-12-05 18:50   ` Alexey Dobriyan

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).