Skip to content

Commit 1fde6f2

Browse files
Alexey Dobriyantorvalds
authored andcommitted
proc: fix /proc/net/* after setns(2)
/proc entries under /proc/net/* can't be cached into dcache because setns(2) can change current net namespace. [akpm@linux-foundation.org: coding-style fixes] [akpm@linux-foundation.org: avoid vim miscolorization] [adobriyan@gmail.com: write test, add dummy ->d_revalidate hook: necessary if /proc/net/* is pinned at setns time] Link: http://lkml.kernel.org/r/20190108192350.GA12034@avx2 Link: http://lkml.kernel.org/r/20190107162336.GA9239@avx2 Fixes: 1da4d37 ("proc: revalidate misc dentries") Signed-off-by: Alexey Dobriyan <adobriyan@gmail.com> Reported-by: Mateusz Stępień <mateusz.stepien@netrounds.com> Reported-by: Ahmad Fatoum <a.fatoum@pengutronix.de> Cc: Al Viro <viro@zeniv.linux.org.uk> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
1 parent 1723058 commit 1fde6f2

File tree

6 files changed

+155
-1
lines changed

6 files changed

+155
-1
lines changed

fs/proc/generic.c

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -256,7 +256,7 @@ struct dentry *proc_lookup_de(struct inode *dir, struct dentry *dentry,
256256
inode = proc_get_inode(dir->i_sb, de);
257257
if (!inode)
258258
return ERR_PTR(-ENOMEM);
259-
d_set_d_op(dentry, &proc_misc_dentry_ops);
259+
d_set_d_op(dentry, de->proc_dops);
260260
return d_splice_alias(inode, dentry);
261261
}
262262
read_unlock(&proc_subdir_lock);
@@ -429,6 +429,8 @@ static struct proc_dir_entry *__proc_create(struct proc_dir_entry **parent,
429429
INIT_LIST_HEAD(&ent->pde_openers);
430430
proc_set_user(ent, (*parent)->uid, (*parent)->gid);
431431

432+
ent->proc_dops = &proc_misc_dentry_ops;
433+
432434
out:
433435
return ent;
434436
}

fs/proc/internal.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ struct proc_dir_entry {
4444
struct completion *pde_unload_completion;
4545
const struct inode_operations *proc_iops;
4646
const struct file_operations *proc_fops;
47+
const struct dentry_operations *proc_dops;
4748
union {
4849
const struct seq_operations *seq_ops;
4950
int (*single_show)(struct seq_file *, void *);

fs/proc/proc_net.c

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,22 @@ static struct net *get_proc_net(const struct inode *inode)
3838
return maybe_get_net(PDE_NET(PDE(inode)));
3939
}
4040

41+
static int proc_net_d_revalidate(struct dentry *dentry, unsigned int flags)
42+
{
43+
return 0;
44+
}
45+
46+
static const struct dentry_operations proc_net_dentry_ops = {
47+
.d_revalidate = proc_net_d_revalidate,
48+
.d_delete = always_delete_dentry,
49+
};
50+
51+
static void pde_force_lookup(struct proc_dir_entry *pde)
52+
{
53+
/* /proc/net/ entries can be changed under us by setns(CLONE_NEWNET) */
54+
pde->proc_dops = &proc_net_dentry_ops;
55+
}
56+
4157
static int seq_open_net(struct inode *inode, struct file *file)
4258
{
4359
unsigned int state_size = PDE(inode)->state_size;
@@ -90,6 +106,7 @@ struct proc_dir_entry *proc_create_net_data(const char *name, umode_t mode,
90106
p = proc_create_reg(name, mode, &parent, data);
91107
if (!p)
92108
return NULL;
109+
pde_force_lookup(p);
93110
p->proc_fops = &proc_net_seq_fops;
94111
p->seq_ops = ops;
95112
p->state_size = state_size;
@@ -133,6 +150,7 @@ struct proc_dir_entry *proc_create_net_data_write(const char *name, umode_t mode
133150
p = proc_create_reg(name, mode, &parent, data);
134151
if (!p)
135152
return NULL;
153+
pde_force_lookup(p);
136154
p->proc_fops = &proc_net_seq_fops;
137155
p->seq_ops = ops;
138156
p->state_size = state_size;
@@ -181,6 +199,7 @@ struct proc_dir_entry *proc_create_net_single(const char *name, umode_t mode,
181199
p = proc_create_reg(name, mode, &parent, data);
182200
if (!p)
183201
return NULL;
202+
pde_force_lookup(p);
184203
p->proc_fops = &proc_net_single_fops;
185204
p->single_show = show;
186205
return proc_register(parent, p);
@@ -223,6 +242,7 @@ struct proc_dir_entry *proc_create_net_single_write(const char *name, umode_t mo
223242
p = proc_create_reg(name, mode, &parent, data);
224243
if (!p)
225244
return NULL;
245+
pde_force_lookup(p);
226246
p->proc_fops = &proc_net_single_fops;
227247
p->single_show = show;
228248
p->write = write;

tools/testing/selftests/proc/.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,4 +10,5 @@
1010
/proc-uptime-002
1111
/read
1212
/self
13+
/setns-dcache
1314
/thread-self

tools/testing/selftests/proc/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ TEST_GEN_PROGS += proc-uptime-001
1414
TEST_GEN_PROGS += proc-uptime-002
1515
TEST_GEN_PROGS += read
1616
TEST_GEN_PROGS += self
17+
TEST_GEN_PROGS += setns-dcache
1718
TEST_GEN_PROGS += thread-self
1819

1920
include ../lib.mk
Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
/*
2+
* Copyright © 2019 Alexey Dobriyan <adobriyan@gmail.com>
3+
*
4+
* Permission to use, copy, modify, and distribute this software for any
5+
* purpose with or without fee is hereby granted, provided that the above
6+
* copyright notice and this permission notice appear in all copies.
7+
*
8+
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9+
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10+
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11+
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12+
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13+
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14+
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15+
*/
16+
/*
17+
* Test that setns(CLONE_NEWNET) points to new /proc/net content even
18+
* if old one is in dcache.
19+
*
20+
* FIXME /proc/net/unix is under CONFIG_UNIX which can be disabled.
21+
*/
22+
#undef NDEBUG
23+
#include <assert.h>
24+
#include <errno.h>
25+
#include <sched.h>
26+
#include <signal.h>
27+
#include <stdio.h>
28+
#include <stdlib.h>
29+
#include <string.h>
30+
#include <unistd.h>
31+
#include <sys/types.h>
32+
#include <sys/stat.h>
33+
#include <fcntl.h>
34+
#include <sys/socket.h>
35+
36+
static pid_t pid = -1;
37+
38+
static void f(void)
39+
{
40+
if (pid > 0) {
41+
kill(pid, SIGTERM);
42+
}
43+
}
44+
45+
int main(void)
46+
{
47+
int fd[2];
48+
char _ = 0;
49+
int nsfd;
50+
51+
atexit(f);
52+
53+
/* Check for priviledges and syscall availability straight away. */
54+
if (unshare(CLONE_NEWNET) == -1) {
55+
if (errno == ENOSYS || errno == EPERM) {
56+
return 4;
57+
}
58+
return 1;
59+
}
60+
/* Distinguisher between two otherwise empty net namespaces. */
61+
if (socket(AF_UNIX, SOCK_STREAM, 0) == -1) {
62+
return 1;
63+
}
64+
65+
if (pipe(fd) == -1) {
66+
return 1;
67+
}
68+
69+
pid = fork();
70+
if (pid == -1) {
71+
return 1;
72+
}
73+
74+
if (pid == 0) {
75+
if (unshare(CLONE_NEWNET) == -1) {
76+
return 1;
77+
}
78+
79+
if (write(fd[1], &_, 1) != 1) {
80+
return 1;
81+
}
82+
83+
pause();
84+
85+
return 0;
86+
}
87+
88+
if (read(fd[0], &_, 1) != 1) {
89+
return 1;
90+
}
91+
92+
{
93+
char buf[64];
94+
snprintf(buf, sizeof(buf), "/proc/%u/ns/net", pid);
95+
nsfd = open(buf, O_RDONLY);
96+
if (nsfd == -1) {
97+
return 1;
98+
}
99+
}
100+
101+
/* Reliably pin dentry into dcache. */
102+
(void)open("/proc/net/unix", O_RDONLY);
103+
104+
if (setns(nsfd, CLONE_NEWNET) == -1) {
105+
return 1;
106+
}
107+
108+
kill(pid, SIGTERM);
109+
pid = 0;
110+
111+
{
112+
char buf[4096];
113+
ssize_t rv;
114+
int fd;
115+
116+
fd = open("/proc/net/unix", O_RDONLY);
117+
if (fd == -1) {
118+
return 1;
119+
}
120+
121+
#define S "Num RefCount Protocol Flags Type St Inode Path\n"
122+
rv = read(fd, buf, sizeof(buf));
123+
124+
assert(rv == strlen(S));
125+
assert(memcmp(buf, S, strlen(S)) == 0);
126+
}
127+
128+
return 0;
129+
}

0 commit comments

Comments
 (0)