Skip to content

Commit a8afca0

Browse files
Eric Dumazetdavem330
authored andcommitted
tcp: md5: protects md5sig_info with RCU
This patch makes sure we use appropriate memory barriers before publishing tp->md5sig_info, allowing tcp_md5_do_lookup() being used from tcp_v4_send_reset() without holding socket lock (upcoming patch from Shawn Lu) Note we also need to respect rcu grace period before its freeing, since we can free socket without this grace period thanks to SLAB_DESTROY_BY_RCU Signed-off-by: Eric Dumazet <[email protected]> Cc: Shawn Lu <[email protected]> Signed-off-by: David S. Miller <[email protected]>
1 parent 41de8d4 commit a8afca0

File tree

4 files changed

+22
-15
lines changed

4 files changed

+22
-15
lines changed

include/linux/tcp.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -463,7 +463,7 @@ struct tcp_sock {
463463
const struct tcp_sock_af_ops *af_specific;
464464

465465
/* TCP MD5 Signature Option information */
466-
struct tcp_md5sig_info *md5sig_info;
466+
struct tcp_md5sig_info __rcu *md5sig_info;
467467
#endif
468468

469469
/* When the cookie options are generated and exchanged, then this

include/net/tcp.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1150,6 +1150,7 @@ struct tcp_md5sig_key {
11501150
/* - sock block */
11511151
struct tcp_md5sig_info {
11521152
struct hlist_head head;
1153+
struct rcu_head rcu;
11531154
};
11541155

11551156
/* - pseudo header */

net/ipv4/tcp_ipv4.c

Lines changed: 20 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -879,14 +879,18 @@ struct tcp_md5sig_key *tcp_md5_do_lookup(struct sock *sk,
879879
struct tcp_md5sig_key *key;
880880
struct hlist_node *pos;
881881
unsigned int size = sizeof(struct in_addr);
882+
struct tcp_md5sig_info *md5sig;
882883

883-
if (!tp->md5sig_info)
884+
/* caller either holds rcu_read_lock() or socket lock */
885+
md5sig = rcu_dereference_check(tp->md5sig_info,
886+
sock_owned_by_user(sk));
887+
if (!md5sig)
884888
return NULL;
885889
#if IS_ENABLED(CONFIG_IPV6)
886890
if (family == AF_INET6)
887891
size = sizeof(struct in6_addr);
888892
#endif
889-
hlist_for_each_entry_rcu(key, pos, &tp->md5sig_info->head, node) {
893+
hlist_for_each_entry_rcu(key, pos, &md5sig->head, node) {
890894
if (key->family != family)
891895
continue;
892896
if (!memcmp(&key->addr, addr, size))
@@ -932,15 +936,16 @@ int tcp_md5_do_add(struct sock *sk, const union tcp_md5_addr *addr,
932936
return 0;
933937
}
934938

935-
md5sig = tp->md5sig_info;
939+
md5sig = rcu_dereference_protected(tp->md5sig_info,
940+
sock_owned_by_user(sk));
936941
if (!md5sig) {
937942
md5sig = kmalloc(sizeof(*md5sig), gfp);
938943
if (!md5sig)
939944
return -ENOMEM;
940945

941946
sk_nocaps_add(sk, NETIF_F_GSO_MASK);
942947
INIT_HLIST_HEAD(&md5sig->head);
943-
tp->md5sig_info = md5sig;
948+
rcu_assign_pointer(tp->md5sig_info, md5sig);
944949
}
945950

946951
key = sock_kmalloc(sk, sizeof(*key), gfp);
@@ -966,14 +971,17 @@ int tcp_md5_do_del(struct sock *sk, const union tcp_md5_addr *addr, int family)
966971
{
967972
struct tcp_sock *tp = tcp_sk(sk);
968973
struct tcp_md5sig_key *key;
974+
struct tcp_md5sig_info *md5sig;
969975

970976
key = tcp_md5_do_lookup(sk, (union tcp_md5_addr *)&addr, AF_INET);
971977
if (!key)
972978
return -ENOENT;
973979
hlist_del_rcu(&key->node);
974980
atomic_sub(sizeof(*key), &sk->sk_omem_alloc);
975981
kfree_rcu(key, rcu);
976-
if (hlist_empty(&tp->md5sig_info->head))
982+
md5sig = rcu_dereference_protected(tp->md5sig_info,
983+
sock_owned_by_user(sk));
984+
if (hlist_empty(&md5sig->head))
977985
tcp_free_md5sig_pool();
978986
return 0;
979987
}
@@ -984,10 +992,13 @@ void tcp_clear_md5_list(struct sock *sk)
984992
struct tcp_sock *tp = tcp_sk(sk);
985993
struct tcp_md5sig_key *key;
986994
struct hlist_node *pos, *n;
995+
struct tcp_md5sig_info *md5sig;
987996

988-
if (!hlist_empty(&tp->md5sig_info->head))
997+
md5sig = rcu_dereference_protected(tp->md5sig_info, 1);
998+
999+
if (!hlist_empty(&md5sig->head))
9891000
tcp_free_md5sig_pool();
990-
hlist_for_each_entry_safe(key, pos, n, &tp->md5sig_info->head, node) {
1001+
hlist_for_each_entry_safe(key, pos, n, &md5sig->head, node) {
9911002
hlist_del_rcu(&key->node);
9921003
atomic_sub(sizeof(*key), &sk->sk_omem_alloc);
9931004
kfree_rcu(key, rcu);
@@ -1009,12 +1020,9 @@ static int tcp_v4_parse_md5_keys(struct sock *sk, char __user *optval,
10091020
if (sin->sin_family != AF_INET)
10101021
return -EINVAL;
10111022

1012-
if (!cmd.tcpm_key || !cmd.tcpm_keylen) {
1013-
if (!tcp_sk(sk)->md5sig_info)
1014-
return -ENOENT;
1023+
if (!cmd.tcpm_key || !cmd.tcpm_keylen)
10151024
return tcp_md5_do_del(sk, (union tcp_md5_addr *)&sin->sin_addr.s_addr,
10161025
AF_INET);
1017-
}
10181026

10191027
if (cmd.tcpm_keylen > TCP_MD5SIG_MAXKEYLEN)
10201028
return -EINVAL;
@@ -1896,7 +1904,7 @@ void tcp_v4_destroy_sock(struct sock *sk)
18961904
/* Clean up the MD5 key list, if any */
18971905
if (tp->md5sig_info) {
18981906
tcp_clear_md5_list(sk);
1899-
kfree(tp->md5sig_info);
1907+
kfree_rcu(tp->md5sig_info, rcu);
19001908
tp->md5sig_info = NULL;
19011909
}
19021910
#endif

net/ipv6/tcp_ipv6.c

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -571,8 +571,6 @@ static int tcp_v6_parse_md5_keys (struct sock *sk, char __user *optval,
571571
return -EINVAL;
572572

573573
if (!cmd.tcpm_keylen) {
574-
if (!tcp_sk(sk)->md5sig_info)
575-
return -ENOENT;
576574
if (ipv6_addr_v4mapped(&sin6->sin6_addr))
577575
return tcp_md5_do_del(sk, (union tcp_md5_addr *)&sin6->sin6_addr.s6_addr32[3],
578576
AF_INET);

0 commit comments

Comments
 (0)