mirror of
https://github.com/torvalds/linux.git
synced 2024-11-22 04:02:20 +00:00
net: add networking namespace refcount tracker
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. Signed-off-by: Eric Dumazet <edumazet@google.com> Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
parent
e5d75fc20b
commit
9ba74e6c9e
@ -48,7 +48,7 @@
|
|||||||
#include <uapi/linux/pkt_cls.h>
|
#include <uapi/linux/pkt_cls.h>
|
||||||
#include <linux/hashtable.h>
|
#include <linux/hashtable.h>
|
||||||
#include <linux/rbtree.h>
|
#include <linux/rbtree.h>
|
||||||
#include <linux/ref_tracker.h>
|
#include <net/net_trackers.h>
|
||||||
|
|
||||||
struct netpoll_info;
|
struct netpoll_info;
|
||||||
struct device;
|
struct device;
|
||||||
@ -300,13 +300,6 @@ enum netdev_state_t {
|
|||||||
__LINK_STATE_TESTING,
|
__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 gro_list {
|
||||||
struct list_head list;
|
struct list_head list;
|
||||||
int count;
|
int count;
|
||||||
|
@ -34,6 +34,7 @@
|
|||||||
#include <net/netns/smc.h>
|
#include <net/netns/smc.h>
|
||||||
#include <net/netns/bpf.h>
|
#include <net/netns/bpf.h>
|
||||||
#include <net/netns/mctp.h>
|
#include <net/netns/mctp.h>
|
||||||
|
#include <net/net_trackers.h>
|
||||||
#include <linux/ns_common.h>
|
#include <linux/ns_common.h>
|
||||||
#include <linux/idr.h>
|
#include <linux/idr.h>
|
||||||
#include <linux/skbuff.h>
|
#include <linux/skbuff.h>
|
||||||
@ -87,6 +88,7 @@ struct net {
|
|||||||
struct idr netns_ids;
|
struct idr netns_ids;
|
||||||
|
|
||||||
struct ns_common ns;
|
struct ns_common ns;
|
||||||
|
struct ref_tracker_dir refcnt_tracker;
|
||||||
|
|
||||||
struct list_head dev_base_head;
|
struct list_head dev_base_head;
|
||||||
struct proc_dir_entry *proc_net;
|
struct proc_dir_entry *proc_net;
|
||||||
@ -240,6 +242,7 @@ void ipx_unregister_sysctl(void);
|
|||||||
#ifdef CONFIG_NET_NS
|
#ifdef CONFIG_NET_NS
|
||||||
void __put_net(struct net *net);
|
void __put_net(struct net *net);
|
||||||
|
|
||||||
|
/* Try using get_net_track() instead */
|
||||||
static inline struct net *get_net(struct net *net)
|
static inline struct net *get_net(struct net *net)
|
||||||
{
|
{
|
||||||
refcount_inc(&net->ns.count);
|
refcount_inc(&net->ns.count);
|
||||||
@ -258,6 +261,7 @@ static inline struct net *maybe_get_net(struct net *net)
|
|||||||
return net;
|
return net;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Try using put_net_track() instead */
|
||||||
static inline void put_net(struct net *net)
|
static inline void put_net(struct net *net)
|
||||||
{
|
{
|
||||||
if (refcount_dec_and_test(&net->ns.count))
|
if (refcount_dec_and_test(&net->ns.count))
|
||||||
@ -308,6 +312,36 @@ static inline int check_net(const struct net *net)
|
|||||||
#endif
|
#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 {
|
typedef struct {
|
||||||
#ifdef CONFIG_NET_NS
|
#ifdef CONFIG_NET_NS
|
||||||
struct net *net;
|
struct net *net;
|
||||||
|
18
include/net/net_trackers.h
Normal file
18
include/net/net_trackers.h
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
/* SPDX-License-Identifier: GPL-2.0 */
|
||||||
|
#ifndef __NET_NET_TRACKERS_H
|
||||||
|
#define __NET_NET_TRACKERS_H
|
||||||
|
#include <linux/ref_tracker.h>
|
||||||
|
|
||||||
|
#ifdef CONFIG_NET_DEV_REFCNT_TRACKER
|
||||||
|
typedef struct ref_tracker *netdevice_tracker;
|
||||||
|
#else
|
||||||
|
typedef struct {} netdevice_tracker;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef CONFIG_NET_NS_REFCNT_TRACKER
|
||||||
|
typedef struct ref_tracker *netns_tracker;
|
||||||
|
#else
|
||||||
|
typedef struct {} netns_tracker;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* __NET_NET_TRACKERS_H */
|
@ -8,3 +8,12 @@ config NET_DEV_REFCNT_TRACKER
|
|||||||
help
|
help
|
||||||
Enable debugging feature to track device references.
|
Enable debugging feature to track device references.
|
||||||
This adds memory and cpu costs.
|
This adds memory and cpu costs.
|
||||||
|
|
||||||
|
config NET_NS_REFCNT_TRACKER
|
||||||
|
bool "Enable networking namespace refcount tracking"
|
||||||
|
depends on DEBUG_KERNEL && STACKTRACE_SUPPORT
|
||||||
|
select REF_TRACKER
|
||||||
|
default n
|
||||||
|
help
|
||||||
|
Enable debugging feature to track netns references.
|
||||||
|
This adds memory and cpu costs.
|
||||||
|
@ -311,6 +311,8 @@ static __net_init int setup_net(struct net *net, struct user_namespace *user_ns)
|
|||||||
LIST_HEAD(net_exit_list);
|
LIST_HEAD(net_exit_list);
|
||||||
|
|
||||||
refcount_set(&net->ns.count, 1);
|
refcount_set(&net->ns.count, 1);
|
||||||
|
ref_tracker_dir_init(&net->refcnt_tracker, 128);
|
||||||
|
|
||||||
refcount_set(&net->passive, 1);
|
refcount_set(&net->passive, 1);
|
||||||
get_random_bytes(&net->hash_mix, sizeof(u32));
|
get_random_bytes(&net->hash_mix, sizeof(u32));
|
||||||
preempt_disable();
|
preempt_disable();
|
||||||
@ -635,6 +637,7 @@ static DECLARE_WORK(net_cleanup_work, cleanup_net);
|
|||||||
|
|
||||||
void __put_net(struct net *net)
|
void __put_net(struct net *net)
|
||||||
{
|
{
|
||||||
|
ref_tracker_dir_exit(&net->refcnt_tracker);
|
||||||
/* Cleanup the network namespace in process context */
|
/* Cleanup the network namespace in process context */
|
||||||
if (llist_add(&net->cleanup_list, &cleanup_list))
|
if (llist_add(&net->cleanup_list, &cleanup_list))
|
||||||
queue_work(netns_wq, &net_cleanup_work);
|
queue_work(netns_wq, &net_cleanup_work);
|
||||||
|
Loading…
Reference in New Issue
Block a user