Commit 077130c0cf7d5ba1992f5b51b96136d7b1c8aad5
Committed by
David S. Miller
1 parent
4fabcd7118
Exists in
master
and in
39 other branches
[NET]: Fix race when opening a proc file while a network namespace is exiting.
The problem: proc_net files remember which network namespace the are against but do not remember hold a reference count (as that would pin the network namespace). So we currently have a small window where the reference count on a network namespace may be incremented when opening a /proc file when it has already gone to zero. To fix this introduce maybe_get_net and get_proc_net. maybe_get_net increments the network namespace reference count only if it is greater then zero, ensuring we don't increment a reference count after it has gone to zero. get_proc_net handles all of the magic to go from a proc inode to the network namespace instance and call maybe_get_net on it. PROC_NET the old accessor is removed so that we don't get confused and use the wrong helper function. Then I fix up the callers to use get_proc_net and handle the case case where get_proc_net returns NULL. In that case I return -ENXIO because effectively the network namespace has already gone away so the files we are trying to access don't exist anymore. Signed-off-by: Eric W. Biederman <ebiederm@xmission.com> Acked-by: Paul E. McKenney <paulmck@us.ibm.com> Signed-off-by: David S. Miller <davem@davemloft.net>
Showing 7 changed files with 39 additions and 8 deletions Side-by-side Diff
fs/proc/proc_net.c
... | ... | @@ -51,6 +51,12 @@ |
51 | 51 | } |
52 | 52 | EXPORT_SYMBOL_GPL(proc_net_remove); |
53 | 53 | |
54 | +struct net *get_proc_net(const struct inode *inode) | |
55 | +{ | |
56 | + return maybe_get_net(PDE_NET(PDE(inode))); | |
57 | +} | |
58 | +EXPORT_SYMBOL_GPL(get_proc_net); | |
59 | + | |
54 | 60 | static struct proc_dir_entry *proc_net_shadow; |
55 | 61 | |
56 | 62 | static struct dentry *proc_net_shadow_dentry(struct dentry *parent, |
include/linux/proc_fs.h
... | ... | @@ -270,10 +270,7 @@ |
270 | 270 | return pde->parent->data; |
271 | 271 | } |
272 | 272 | |
273 | -static inline struct net *PROC_NET(const struct inode *inode) | |
274 | -{ | |
275 | - return PDE_NET(PDE(inode)); | |
276 | -} | |
273 | +struct net *get_proc_net(const struct inode *inode); | |
277 | 274 | |
278 | 275 | struct proc_maps_private { |
279 | 276 | struct pid *pid; |
include/net/net_namespace.h
... | ... | @@ -46,6 +46,18 @@ |
46 | 46 | return net; |
47 | 47 | } |
48 | 48 | |
49 | +static inline struct net *maybe_get_net(struct net *net) | |
50 | +{ | |
51 | + /* Used when we know struct net exists but we | |
52 | + * aren't guaranteed a previous reference count | |
53 | + * exists. If the reference count is zero this | |
54 | + * function fails and returns NULL. | |
55 | + */ | |
56 | + if (!atomic_inc_not_zero(&net->count)) | |
57 | + net = NULL; | |
58 | + return net; | |
59 | +} | |
60 | + | |
49 | 61 | static inline void put_net(struct net *net) |
50 | 62 | { |
51 | 63 | if (atomic_dec_and_test(&net->count)) |
net/core/dev.c
... | ... | @@ -2464,7 +2464,11 @@ |
2464 | 2464 | res = seq_open(file, &dev_seq_ops); |
2465 | 2465 | if (!res) { |
2466 | 2466 | seq = file->private_data; |
2467 | - seq->private = get_net(PROC_NET(inode)); | |
2467 | + seq->private = get_proc_net(inode); | |
2468 | + if (!seq->private) { | |
2469 | + seq_release(inode, file); | |
2470 | + res = -ENXIO; | |
2471 | + } | |
2468 | 2472 | } |
2469 | 2473 | return res; |
2470 | 2474 | } |
net/core/dev_mcast.c
... | ... | @@ -246,7 +246,11 @@ |
246 | 246 | res = seq_open(file, &dev_mc_seq_ops); |
247 | 247 | if (!res) { |
248 | 248 | seq = file->private_data; |
249 | - seq->private = get_net(PROC_NET(inode)); | |
249 | + seq->private = get_proc_net(inode); | |
250 | + if (!seq->private) { | |
251 | + seq_release(inode, file); | |
252 | + res = -ENXIO; | |
253 | + } | |
250 | 254 | } |
251 | 255 | return res; |
252 | 256 | } |
net/netlink/af_netlink.c
... | ... | @@ -1859,7 +1859,11 @@ |
1859 | 1859 | |
1860 | 1860 | seq = file->private_data; |
1861 | 1861 | seq->private = iter; |
1862 | - iter->net = get_net(PROC_NET(inode)); | |
1862 | + iter->net = get_proc_net(inode); | |
1863 | + if (!iter->net) { | |
1864 | + seq_release_private(inode, file); | |
1865 | + return -ENXIO; | |
1866 | + } | |
1863 | 1867 | return 0; |
1864 | 1868 | } |
1865 | 1869 |
net/wireless/wext.c
... | ... | @@ -678,7 +678,11 @@ |
678 | 678 | res = seq_open(file, &wireless_seq_ops); |
679 | 679 | if (!res) { |
680 | 680 | seq = file->private_data; |
681 | - seq->private = get_net(PROC_NET(inode)); | |
681 | + seq->private = get_proc_net(inode); | |
682 | + if (!seq->private) { | |
683 | + seq_release(inode, file); | |
684 | + res = -ENXIO; | |
685 | + } | |
682 | 686 | } |
683 | 687 | return res; |
684 | 688 | } |