Commit 077130c0cf7d5ba1992f5b51b96136d7b1c8aad5

Authored by Eric W. Biederman
Committed by David S. Miller
1 parent 4fabcd7118

[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

... ... @@ -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))
... ... @@ -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  
... ... @@ -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 }