* /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).