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