Skip to content

Commit fa39032

Browse files
cpaasch-oaikuba-moo
authored andcommitted
net/tcp: Fix socket memory leak in TCP-AO failure handling for IPv6
When tcp_ao_copy_all_matching() fails in tcp_v6_syn_recv_sock() it just exits the function. This ends up causing a memory-leak: unreferenced object 0xffff0000281a8200 (size 2496): comm "softirq", pid 0, jiffies 4295174684 hex dump (first 32 bytes): 7f 00 00 06 7f 00 00 06 00 00 00 00 cb a8 88 13 ................ 0a 00 03 61 00 00 00 00 00 00 00 00 00 00 00 00 ...a............ backtrace (crc 5ebdbe15): kmemleak_alloc+0x44/0xe0 kmem_cache_alloc_noprof+0x248/0x470 sk_prot_alloc+0x48/0x120 sk_clone_lock+0x38/0x3b0 inet_csk_clone_lock+0x34/0x150 tcp_create_openreq_child+0x3c/0x4a8 tcp_v6_syn_recv_sock+0x1c0/0x620 tcp_check_req+0x588/0x790 tcp_v6_rcv+0x5d0/0xc18 ip6_protocol_deliver_rcu+0x2d8/0x4c0 ip6_input_finish+0x74/0x148 ip6_input+0x50/0x118 ip6_sublist_rcv+0x2fc/0x3b0 ipv6_list_rcv+0x114/0x170 __netif_receive_skb_list_core+0x16c/0x200 netif_receive_skb_list_internal+0x1f0/0x2d0 This is because in tcp_v6_syn_recv_sock (and the IPv4 counterpart), when exiting upon error, inet_csk_prepare_forced_close() and tcp_done() need to be called. They make sure the newsk will end up being correctly free'd. tcp_v4_syn_recv_sock() makes this very clear by having the put_and_exit label that takes care of things. So, this patch here makes sure tcp_v4_syn_recv_sock and tcp_v6_syn_recv_sock have similar error-handling and thus fixes the leak for TCP-AO. Fixes: 06b22ef ("net/tcp: Wire TCP-AO to request sockets") Signed-off-by: Christoph Paasch <[email protected]> Reviewed-by: Dmitry Safonov <[email protected]> Link: https://patch.msgid.link/[email protected] Signed-off-by: Jakub Kicinski <[email protected]>
1 parent d2644cb commit fa39032

File tree

1 file changed

+15
-17
lines changed

1 file changed

+15
-17
lines changed

net/ipv6/tcp_ipv6.c

Lines changed: 15 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1431,17 +1431,17 @@ static struct sock *tcp_v6_syn_recv_sock(const struct sock *sk, struct sk_buff *
14311431
ireq = inet_rsk(req);
14321432

14331433
if (sk_acceptq_is_full(sk))
1434-
goto out_overflow;
1434+
goto exit_overflow;
14351435

14361436
if (!dst) {
14371437
dst = inet6_csk_route_req(sk, &fl6, req, IPPROTO_TCP);
14381438
if (!dst)
1439-
goto out;
1439+
goto exit;
14401440
}
14411441

14421442
newsk = tcp_create_openreq_child(sk, req, skb);
14431443
if (!newsk)
1444-
goto out_nonewsk;
1444+
goto exit_nonewsk;
14451445

14461446
/*
14471447
* No need to charge this sock to the relevant IPv6 refcnt debug socks
@@ -1525,25 +1525,19 @@ static struct sock *tcp_v6_syn_recv_sock(const struct sock *sk, struct sk_buff *
15251525
const union tcp_md5_addr *addr;
15261526

15271527
addr = (union tcp_md5_addr *)&newsk->sk_v6_daddr;
1528-
if (tcp_md5_key_copy(newsk, addr, AF_INET6, 128, l3index, key)) {
1529-
inet_csk_prepare_forced_close(newsk);
1530-
tcp_done(newsk);
1531-
goto out;
1532-
}
1528+
if (tcp_md5_key_copy(newsk, addr, AF_INET6, 128, l3index, key))
1529+
goto put_and_exit;
15331530
}
15341531
}
15351532
#endif
15361533
#ifdef CONFIG_TCP_AO
15371534
/* Copy over tcp_ao_info if any */
15381535
if (tcp_ao_copy_all_matching(sk, newsk, req, skb, AF_INET6))
1539-
goto out; /* OOM */
1536+
goto put_and_exit; /* OOM */
15401537
#endif
15411538

1542-
if (__inet_inherit_port(sk, newsk) < 0) {
1543-
inet_csk_prepare_forced_close(newsk);
1544-
tcp_done(newsk);
1545-
goto out;
1546-
}
1539+
if (__inet_inherit_port(sk, newsk) < 0)
1540+
goto put_and_exit;
15471541
*own_req = inet_ehash_nolisten(newsk, req_to_sk(req_unhash),
15481542
&found_dup_sk);
15491543
if (*own_req) {
@@ -1570,13 +1564,17 @@ static struct sock *tcp_v6_syn_recv_sock(const struct sock *sk, struct sk_buff *
15701564

15711565
return newsk;
15721566

1573-
out_overflow:
1567+
exit_overflow:
15741568
__NET_INC_STATS(sock_net(sk), LINUX_MIB_LISTENOVERFLOWS);
1575-
out_nonewsk:
1569+
exit_nonewsk:
15761570
dst_release(dst);
1577-
out:
1571+
exit:
15781572
tcp_listendrop(sk);
15791573
return NULL;
1574+
put_and_exit:
1575+
inet_csk_prepare_forced_close(newsk);
1576+
tcp_done(newsk);
1577+
goto exit;
15801578
}
15811579

15821580
INDIRECT_CALLABLE_DECLARE(struct dst_entry *ipv4_dst_check(struct dst_entry *,

0 commit comments

Comments
 (0)