Commit 3d20408d authored by Jakub Kicinski's avatar Jakub Kicinski
Browse files

Merge branch 'net-netns-refcount-tracking-base-series'

Eric Dumazet says:

====================
net: netns refcount tracking, base series

We have 100+ syzbot reports about netns being dismantled too soon,
still unresolved as of today.

We think a missing get_net() or an extra put_net() is the root cause.

In order to find the bug(s), and be able to spot future ones,
this patch adds CONFIG_NET_NS_REFCNT_TRACKER and new helpers
to precisely pair all put_net() with corresponding get_net().

To use these helpers, each data structure owning a refcount
should also use a "netns_tracker" to pair the get() and put().

Small sections of codes where the get()/put() are in sight
do not need to have a tracker, because they are short lived,
but in theory it is also possible to declare an on-stack tracker.

v2: Include core networking patches only.
====================

Link: https://lore.kernel.org/r/20211210074426.279563-1-eric.dumazet@gmail.com


Signed-off-by: default avatarJakub Kicinski <kuba@kernel.org>
parents e5d75fc2 11b311a8
Loading
Loading
Loading
Loading
+3 −2
Original line number Diff line number Diff line
@@ -173,6 +173,7 @@ struct channel {
	spinlock_t	downl;		/* protects `chan', file.xq dequeue */
	struct ppp	*ppp;		/* ppp unit we're connected to */
	struct net	*chan_net;	/* the net channel belongs to */
	netns_tracker	ns_tracker;
	struct list_head clist;		/* link in list of channels per unit */
	rwlock_t	upl;		/* protects `ppp' and 'bridge' */
	struct channel __rcu *bridge;	/* "bridged" ppp channel */
@@ -2879,7 +2880,7 @@ int ppp_register_net_channel(struct net *net, struct ppp_channel *chan)

	pch->ppp = NULL;
	pch->chan = chan;
	pch->chan_net = get_net(net);
	pch->chan_net = get_net_track(net, &pch->ns_tracker, GFP_KERNEL);
	chan->ppp = pch;
	init_ppp_file(&pch->file, CHANNEL);
	pch->file.hdrlen = chan->hdrlen;
@@ -3519,7 +3520,7 @@ ppp_disconnect_channel(struct channel *pch)
 */
static void ppp_destroy_channel(struct channel *pch)
{
	put_net(pch->chan_net);
	put_net_track(pch->chan_net, &pch->ns_tracker);
	pch->chan_net = NULL;

	atomic_dec(&channel_count);
+16 −3
Original line number Diff line number Diff line
@@ -61,15 +61,27 @@ static int seq_open_net(struct inode *inode, struct file *file)
	}
#ifdef CONFIG_NET_NS
	p->net = net;
	netns_tracker_alloc(net, &p->ns_tracker, GFP_KERNEL);
#endif
	return 0;
}

static void seq_file_net_put_net(struct seq_file *seq)
{
#ifdef CONFIG_NET_NS
	struct seq_net_private *priv = seq->private;

	put_net_track(priv->net, &priv->ns_tracker);
#else
	put_net(&init_net);
#endif
}

static int seq_release_net(struct inode *ino, struct file *f)
{
	struct seq_file *seq = f->private_data;

	put_net(seq_file_net(seq));
	seq_file_net_put_net(seq);
	seq_release_private(ino, f);
	return 0;
}
@@ -87,7 +99,8 @@ int bpf_iter_init_seq_net(void *priv_data, struct bpf_iter_aux_info *aux)
#ifdef CONFIG_NET_NS
	struct seq_net_private *p = priv_data;

	p->net = get_net(current->nsproxy->net_ns);
	p->net = get_net_track(current->nsproxy->net_ns, &p->ns_tracker,
			       GFP_KERNEL);
#endif
	return 0;
}
@@ -97,7 +110,7 @@ void bpf_iter_fini_seq_net(void *priv_data)
#ifdef CONFIG_NET_NS
	struct seq_net_private *p = priv_data;

	put_net(p->net);
	put_net_track(p->net, &p->ns_tracker);
#endif
}

+1 −8
Original line number Diff line number Diff line
@@ -48,7 +48,7 @@
#include <uapi/linux/pkt_cls.h>
#include <linux/hashtable.h>
#include <linux/rbtree.h>
#include <linux/ref_tracker.h>
#include <net/net_trackers.h>

struct netpoll_info;
struct device;
@@ -300,13 +300,6 @@ enum netdev_state_t {
	__LINK_STATE_TESTING,
};


#ifdef CONFIG_NET_DEV_REFCNT_TRACKER
typedef struct ref_tracker *netdevice_tracker;
#else
typedef struct {} netdevice_tracker;
#endif

struct gro_list {
	struct list_head	list;
	int			count;
+2 −1
Original line number Diff line number Diff line
@@ -10,6 +10,7 @@ extern struct net init_net;
struct seq_net_private {
#ifdef CONFIG_NET_NS
	struct net	*net;
	netns_tracker	ns_tracker;
#endif
};

+34 −0
Original line number Diff line number Diff line
@@ -34,6 +34,7 @@
#include <net/netns/smc.h>
#include <net/netns/bpf.h>
#include <net/netns/mctp.h>
#include <net/net_trackers.h>
#include <linux/ns_common.h>
#include <linux/idr.h>
#include <linux/skbuff.h>
@@ -87,6 +88,7 @@ struct net {
	struct idr		netns_ids;

	struct ns_common	ns;
	struct ref_tracker_dir  refcnt_tracker;

	struct list_head 	dev_base_head;
	struct proc_dir_entry 	*proc_net;
@@ -240,6 +242,7 @@ void ipx_unregister_sysctl(void);
#ifdef CONFIG_NET_NS
void __put_net(struct net *net);

/* Try using get_net_track() instead */
static inline struct net *get_net(struct net *net)
{
	refcount_inc(&net->ns.count);
@@ -258,6 +261,7 @@ static inline struct net *maybe_get_net(struct net *net)
	return net;
}

/* Try using put_net_track() instead */
static inline void put_net(struct net *net)
{
	if (refcount_dec_and_test(&net->ns.count))
@@ -308,6 +312,36 @@ static inline int check_net(const struct net *net)
#endif


static inline void netns_tracker_alloc(struct net *net,
				       netns_tracker *tracker, gfp_t gfp)
{
#ifdef CONFIG_NET_NS_REFCNT_TRACKER
	ref_tracker_alloc(&net->refcnt_tracker, tracker, gfp);
#endif
}

static inline void netns_tracker_free(struct net *net,
				      netns_tracker *tracker)
{
#ifdef CONFIG_NET_NS_REFCNT_TRACKER
       ref_tracker_free(&net->refcnt_tracker, tracker);
#endif
}

static inline struct net *get_net_track(struct net *net,
					netns_tracker *tracker, gfp_t gfp)
{
	get_net(net);
	netns_tracker_alloc(net, tracker, gfp);
	return net;
}

static inline void put_net_track(struct net *net, netns_tracker *tracker)
{
	netns_tracker_free(net, tracker);
	put_net(net);
}

typedef struct {
#ifdef CONFIG_NET_NS
	struct net *net;
Loading