aboutsummaryrefslogtreecommitdiffstats
path: root/net/ipv4/tcp_ipv4.c
diff options
context:
space:
mode:
authorEric Dumazet <eric.dumazet@gmail.com>2012-01-31 13:45:40 -0500
committerDavid S. Miller <davem@davemloft.net>2012-02-01 02:11:47 -0500
commita8afca032998850ec63e83d555cdcf0eb5680cd6 (patch)
tree012c06f6b3a2bfa6a908954a671b1e36e2198eb6 /net/ipv4/tcp_ipv4.c
parent41de8d4cff21a2e81e3d9ff66f5f7c903f9c3ab1 (diff)
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 <eric.dumazet@gmail.com> Cc: Shawn Lu <shawn.lu@ericsson.com> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/ipv4/tcp_ipv4.c')
-rw-r--r--net/ipv4/tcp_ipv4.c32
1 files changed, 20 insertions, 12 deletions
diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c
index da5d3226771b..567cca9b30df 100644
--- a/net/ipv4/tcp_ipv4.c
+++ b/net/ipv4/tcp_ipv4.c
@@ -879,14 +879,18 @@ struct tcp_md5sig_key *tcp_md5_do_lookup(struct sock *sk,
879 struct tcp_md5sig_key *key; 879 struct tcp_md5sig_key *key;
880 struct hlist_node *pos; 880 struct hlist_node *pos;
881 unsigned int size = sizeof(struct in_addr); 881 unsigned int size = sizeof(struct in_addr);
882 struct tcp_md5sig_info *md5sig;
882 883
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)
884 return NULL; 888 return NULL;
885#if IS_ENABLED(CONFIG_IPV6) 889#if IS_ENABLED(CONFIG_IPV6)
886 if (family == AF_INET6) 890 if (family == AF_INET6)
887 size = sizeof(struct in6_addr); 891 size = sizeof(struct in6_addr);
888#endif 892#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) {
890 if (key->family != family) 894 if (key->family != family)
891 continue; 895 continue;
892 if (!memcmp(&key->addr, addr, size)) 896 if (!memcmp(&key->addr, addr, size))
@@ -932,7 +936,8 @@ int tcp_md5_do_add(struct sock *sk, const union tcp_md5_addr *addr,
932 return 0; 936 return 0;
933 } 937 }
934 938
935 md5sig = tp->md5sig_info; 939 md5sig = rcu_dereference_protected(tp->md5sig_info,
940 sock_owned_by_user(sk));
936 if (!md5sig) { 941 if (!md5sig) {
937 md5sig = kmalloc(sizeof(*md5sig), gfp); 942 md5sig = kmalloc(sizeof(*md5sig), gfp);
938 if (!md5sig) 943 if (!md5sig)
@@ -940,7 +945,7 @@ int tcp_md5_do_add(struct sock *sk, const union tcp_md5_addr *addr,
940 945
941 sk_nocaps_add(sk, NETIF_F_GSO_MASK); 946 sk_nocaps_add(sk, NETIF_F_GSO_MASK);
942 INIT_HLIST_HEAD(&md5sig->head); 947 INIT_HLIST_HEAD(&md5sig->head);
943 tp->md5sig_info = md5sig; 948 rcu_assign_pointer(tp->md5sig_info, md5sig);
944 } 949 }
945 950
946 key = sock_kmalloc(sk, sizeof(*key), gfp); 951 key = sock_kmalloc(sk, sizeof(*key), gfp);
@@ -966,6 +971,7 @@ int tcp_md5_do_del(struct sock *sk, const union tcp_md5_addr *addr, int family)
966{ 971{
967 struct tcp_sock *tp = tcp_sk(sk); 972 struct tcp_sock *tp = tcp_sk(sk);
968 struct tcp_md5sig_key *key; 973 struct tcp_md5sig_key *key;
974 struct tcp_md5sig_info *md5sig;
969 975
970 key = tcp_md5_do_lookup(sk, (union tcp_md5_addr *)&addr, AF_INET); 976 key = tcp_md5_do_lookup(sk, (union tcp_md5_addr *)&addr, AF_INET);
971 if (!key) 977 if (!key)
@@ -973,7 +979,9 @@ int tcp_md5_do_del(struct sock *sk, const union tcp_md5_addr *addr, int family)
973 hlist_del_rcu(&key->node); 979 hlist_del_rcu(&key->node);
974 atomic_sub(sizeof(*key), &sk->sk_omem_alloc); 980 atomic_sub(sizeof(*key), &sk->sk_omem_alloc);
975 kfree_rcu(key, rcu); 981 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))
977 tcp_free_md5sig_pool(); 985 tcp_free_md5sig_pool();
978 return 0; 986 return 0;
979} 987}
@@ -984,10 +992,13 @@ void tcp_clear_md5_list(struct sock *sk)
984 struct tcp_sock *tp = tcp_sk(sk); 992 struct tcp_sock *tp = tcp_sk(sk);
985 struct tcp_md5sig_key *key; 993 struct tcp_md5sig_key *key;
986 struct hlist_node *pos, *n; 994 struct hlist_node *pos, *n;
995 struct tcp_md5sig_info *md5sig;
987 996
988 if (!hlist_empty(&tp->md5sig_info->head)) 997 md5sig = rcu_dereference_protected(tp->md5sig_info, 1);
998
999 if (!hlist_empty(&md5sig->head))
989 tcp_free_md5sig_pool(); 1000 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) {
991 hlist_del_rcu(&key->node); 1002 hlist_del_rcu(&key->node);
992 atomic_sub(sizeof(*key), &sk->sk_omem_alloc); 1003 atomic_sub(sizeof(*key), &sk->sk_omem_alloc);
993 kfree_rcu(key, rcu); 1004 kfree_rcu(key, rcu);
@@ -1009,12 +1020,9 @@ static int tcp_v4_parse_md5_keys(struct sock *sk, char __user *optval,
1009 if (sin->sin_family != AF_INET) 1020 if (sin->sin_family != AF_INET)
1010 return -EINVAL; 1021 return -EINVAL;
1011 1022
1012 if (!cmd.tcpm_key || !cmd.tcpm_keylen) { 1023 if (!cmd.tcpm_key || !cmd.tcpm_keylen)
1013 if (!tcp_sk(sk)->md5sig_info)
1014 return -ENOENT;
1015 return tcp_md5_do_del(sk, (union tcp_md5_addr *)&sin->sin_addr.s_addr, 1024 return tcp_md5_do_del(sk, (union tcp_md5_addr *)&sin->sin_addr.s_addr,
1016 AF_INET); 1025 AF_INET);
1017 }
1018 1026
1019 if (cmd.tcpm_keylen > TCP_MD5SIG_MAXKEYLEN) 1027 if (cmd.tcpm_keylen > TCP_MD5SIG_MAXKEYLEN)
1020 return -EINVAL; 1028 return -EINVAL;
@@ -1896,7 +1904,7 @@ void tcp_v4_destroy_sock(struct sock *sk)
1896 /* Clean up the MD5 key list, if any */ 1904 /* Clean up the MD5 key list, if any */
1897 if (tp->md5sig_info) { 1905 if (tp->md5sig_info) {
1898 tcp_clear_md5_list(sk); 1906 tcp_clear_md5_list(sk);
1899 kfree(tp->md5sig_info); 1907 kfree_rcu(tp->md5sig_info, rcu);
1900 tp->md5sig_info = NULL; 1908 tp->md5sig_info = NULL;
1901 } 1909 }
1902#endif 1910#endif