Skip to content

Commit 0cafd77

Browse files
edumazetdavem330
authored andcommitted
net: add a refcount tracker for kernel sockets
Commit ffa84b5 ("net: add netns refcount tracker to struct sock") added a tracker to sockets, but did not track kernel sockets. We still have syzbot reports hinting about netns being destroyed while some kernel TCP sockets had not been dismantled. This patch tracks kernel sockets, and adds a ref_tracker_dir_print() call to net_free() right before the netns is freed. Normally, each layer is responsible for properly releasing its kernel sockets before last call to net_free(). This debugging facility is enabled with CONFIG_NET_NS_REFCNT_TRACKER=y Signed-off-by: Eric Dumazet <[email protected]> Reviewed-by: Kuniyuki Iwashima <[email protected]> Tested-by: Kuniyuki Iwashima <[email protected]> Signed-off-by: David S. Miller <[email protected]>
1 parent b29e0de commit 0cafd77

File tree

5 files changed

+55
-8
lines changed

5 files changed

+55
-8
lines changed

include/net/net_namespace.h

Lines changed: 22 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,9 @@ struct net {
9292

9393
struct ns_common ns;
9494
struct ref_tracker_dir refcnt_tracker;
95-
95+
struct ref_tracker_dir notrefcnt_tracker; /* tracker for objects not
96+
* refcounted against netns
97+
*/
9698
struct list_head dev_base_head;
9799
struct proc_dir_entry *proc_net;
98100
struct proc_dir_entry *proc_net_stat;
@@ -320,19 +322,31 @@ static inline int check_net(const struct net *net)
320322
#endif
321323

322324

323-
static inline void netns_tracker_alloc(struct net *net,
324-
netns_tracker *tracker, gfp_t gfp)
325+
static inline void __netns_tracker_alloc(struct net *net,
326+
netns_tracker *tracker,
327+
bool refcounted,
328+
gfp_t gfp)
325329
{
326330
#ifdef CONFIG_NET_NS_REFCNT_TRACKER
327-
ref_tracker_alloc(&net->refcnt_tracker, tracker, gfp);
331+
ref_tracker_alloc(refcounted ? &net->refcnt_tracker :
332+
&net->notrefcnt_tracker,
333+
tracker, gfp);
328334
#endif
329335
}
330336

331-
static inline void netns_tracker_free(struct net *net,
332-
netns_tracker *tracker)
337+
static inline void netns_tracker_alloc(struct net *net, netns_tracker *tracker,
338+
gfp_t gfp)
339+
{
340+
__netns_tracker_alloc(net, tracker, true, gfp);
341+
}
342+
343+
static inline void __netns_tracker_free(struct net *net,
344+
netns_tracker *tracker,
345+
bool refcounted)
333346
{
334347
#ifdef CONFIG_NET_NS_REFCNT_TRACKER
335-
ref_tracker_free(&net->refcnt_tracker, tracker);
348+
ref_tracker_free(refcounted ? &net->refcnt_tracker :
349+
&net->notrefcnt_tracker, tracker);
336350
#endif
337351
}
338352

@@ -346,7 +360,7 @@ static inline struct net *get_net_track(struct net *net,
346360

347361
static inline void put_net_track(struct net *net, netns_tracker *tracker)
348362
{
349-
netns_tracker_free(net, tracker);
363+
__netns_tracker_free(net, tracker, true);
350364
put_net(net);
351365
}
352366

net/core/net_namespace.c

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -309,6 +309,7 @@ static __net_init int setup_net(struct net *net, struct user_namespace *user_ns)
309309

310310
refcount_set(&net->ns.count, 1);
311311
ref_tracker_dir_init(&net->refcnt_tracker, 128);
312+
ref_tracker_dir_init(&net->notrefcnt_tracker, 128);
312313

313314
refcount_set(&net->passive, 1);
314315
get_random_bytes(&net->hash_mix, sizeof(u32));
@@ -429,6 +430,10 @@ static void net_free(struct net *net)
429430
{
430431
if (refcount_dec_and_test(&net->passive)) {
431432
kfree(rcu_access_pointer(net->gen));
433+
434+
/* There should not be any trackers left there. */
435+
ref_tracker_dir_exit(&net->notrefcnt_tracker);
436+
432437
kmem_cache_free(net_cachep, net);
433438
}
434439
}

net/core/sock.c

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2094,6 +2094,9 @@ struct sock *sk_alloc(struct net *net, int family, gfp_t priority,
20942094
if (likely(sk->sk_net_refcnt)) {
20952095
get_net_track(net, &sk->ns_tracker, priority);
20962096
sock_inuse_add(net, 1);
2097+
} else {
2098+
__netns_tracker_alloc(net, &sk->ns_tracker,
2099+
false, priority);
20972100
}
20982101

20992102
sock_net_set(sk, net);
@@ -2149,6 +2152,9 @@ static void __sk_destruct(struct rcu_head *head)
21492152

21502153
if (likely(sk->sk_net_refcnt))
21512154
put_net_track(sock_net(sk), &sk->ns_tracker);
2155+
else
2156+
__netns_tracker_free(sock_net(sk), &sk->ns_tracker, false);
2157+
21522158
sk_prot_free(sk->sk_prot_creator, sk);
21532159
}
21542160

@@ -2237,6 +2243,14 @@ struct sock *sk_clone_lock(const struct sock *sk, const gfp_t priority)
22372243
if (likely(newsk->sk_net_refcnt)) {
22382244
get_net_track(sock_net(newsk), &newsk->ns_tracker, priority);
22392245
sock_inuse_add(sock_net(newsk), 1);
2246+
} else {
2247+
/* Kernel sockets are not elevating the struct net refcount.
2248+
* Instead, use a tracker to more easily detect if a layer
2249+
* is not properly dismantling its kernel sockets at netns
2250+
* destroy time.
2251+
*/
2252+
__netns_tracker_alloc(sock_net(newsk), &newsk->ns_tracker,
2253+
false, priority);
22402254
}
22412255
sk_node_init(&newsk->sk_node);
22422256
sock_lock_init(newsk);

net/netlink/af_netlink.c

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -812,6 +812,17 @@ static int netlink_release(struct socket *sock)
812812
}
813813

814814
sock_prot_inuse_add(sock_net(sk), &netlink_proto, -1);
815+
816+
/* Because struct net might disappear soon, do not keep a pointer. */
817+
if (!sk->sk_net_refcnt && sock_net(sk) != &init_net) {
818+
__netns_tracker_free(sock_net(sk), &sk->ns_tracker, false);
819+
/* Because of deferred_put_nlk_sk and use of work queue,
820+
* it is possible netns will be freed before this socket.
821+
*/
822+
sock_net_set(sk, &init_net);
823+
__netns_tracker_alloc(&init_net, &sk->ns_tracker,
824+
false, GFP_KERNEL);
825+
}
815826
call_rcu(&nlk->rcu, deferred_put_nlk_sk);
816827
return 0;
817828
}

net/rds/tcp.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -503,6 +503,9 @@ bool rds_tcp_tune(struct socket *sock)
503503
release_sock(sk);
504504
return false;
505505
}
506+
/* Update ns_tracker to current stack trace and refcounted tracker */
507+
__netns_tracker_free(net, &sk->ns_tracker, false);
508+
506509
sk->sk_net_refcnt = 1;
507510
netns_tracker_alloc(net, &sk->ns_tracker, GFP_KERNEL);
508511
sock_inuse_add(net, 1);

0 commit comments

Comments
 (0)