net: add networking namespace refcount tracker
authorEric Dumazet <edumazet@google.com>
Fri, 10 Dec 2021 07:44:21 +0000 (23:44 -0800)
committerJakub Kicinski <kuba@kernel.org>
Fri, 10 Dec 2021 14:38:26 +0000 (06:38 -0800)
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>
include/linux/netdevice.h
include/net/net_namespace.h
include/net/net_trackers.h [new file with mode: 0644]
net/Kconfig.debug
net/core/net_namespace.c

index 1a748ee..235d5d0 100644 (file)
@@ -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;
index bb5fa59..5b61c46 100644 (file)
@@ -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;
diff --git a/include/net/net_trackers.h b/include/net/net_trackers.h
new file mode 100644 (file)
index 0000000..d94c76c
--- /dev/null
@@ -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 */
index fb5c70e..2f50611 100644 (file)
@@ -8,3 +8,12 @@ config NET_DEV_REFCNT_TRACKER
        help
          Enable debugging feature to track device references.
          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.
index 202fa5e..9b7171c 100644 (file)
@@ -311,6 +311,8 @@ static __net_init int setup_net(struct net *net, struct user_namespace *user_ns)
        LIST_HEAD(net_exit_list);
 
        refcount_set(&net->ns.count, 1);
+       ref_tracker_dir_init(&net->refcnt_tracker, 128);
+
        refcount_set(&net->passive, 1);
        get_random_bytes(&net->hash_mix, sizeof(u32));
        preempt_disable();
@@ -635,6 +637,7 @@ static DECLARE_WORK(net_cleanup_work, cleanup_net);
 
 void __put_net(struct net *net)
 {
+       ref_tracker_dir_exit(&net->refcnt_tracker);
        /* Cleanup the network namespace in process context */
        if (llist_add(&net->cleanup_list, &cleanup_list))
                queue_work(netns_wq, &net_cleanup_work);