struct tcp_md5sig_key *key;
        struct hlist_node *pos;
        unsigned int size = sizeof(struct in_addr);
+       struct tcp_md5sig_info *md5sig;
 
-       if (!tp->md5sig_info)
+       /* caller either holds rcu_read_lock() or socket lock */
+       md5sig = rcu_dereference_check(tp->md5sig_info,
+                                      sock_owned_by_user(sk));
+       if (!md5sig)
                return NULL;
 #if IS_ENABLED(CONFIG_IPV6)
        if (family == AF_INET6)
                size = sizeof(struct in6_addr);
 #endif
-       hlist_for_each_entry_rcu(key, pos, &tp->md5sig_info->head, node) {
+       hlist_for_each_entry_rcu(key, pos, &md5sig->head, node) {
                if (key->family != family)
                        continue;
                if (!memcmp(&key->addr, addr, size))
                return 0;
        }
 
-       md5sig = tp->md5sig_info;
+       md5sig = rcu_dereference_protected(tp->md5sig_info,
+                                          sock_owned_by_user(sk));
        if (!md5sig) {
                md5sig = kmalloc(sizeof(*md5sig), gfp);
                if (!md5sig)
 
                sk_nocaps_add(sk, NETIF_F_GSO_MASK);
                INIT_HLIST_HEAD(&md5sig->head);
-               tp->md5sig_info = md5sig;
+               rcu_assign_pointer(tp->md5sig_info, md5sig);
        }
 
        key = sock_kmalloc(sk, sizeof(*key), gfp);
 {
        struct tcp_sock *tp = tcp_sk(sk);
        struct tcp_md5sig_key *key;
+       struct tcp_md5sig_info *md5sig;
 
        key = tcp_md5_do_lookup(sk, (union tcp_md5_addr *)&addr, AF_INET);
        if (!key)
        hlist_del_rcu(&key->node);
        atomic_sub(sizeof(*key), &sk->sk_omem_alloc);
        kfree_rcu(key, rcu);
-       if (hlist_empty(&tp->md5sig_info->head))
+       md5sig = rcu_dereference_protected(tp->md5sig_info,
+                                          sock_owned_by_user(sk));
+       if (hlist_empty(&md5sig->head))
                tcp_free_md5sig_pool();
        return 0;
 }
        struct tcp_sock *tp = tcp_sk(sk);
        struct tcp_md5sig_key *key;
        struct hlist_node *pos, *n;
+       struct tcp_md5sig_info *md5sig;
 
-       if (!hlist_empty(&tp->md5sig_info->head))
+       md5sig = rcu_dereference_protected(tp->md5sig_info, 1);
+
+       if (!hlist_empty(&md5sig->head))
                tcp_free_md5sig_pool();
-       hlist_for_each_entry_safe(key, pos, n, &tp->md5sig_info->head, node) {
+       hlist_for_each_entry_safe(key, pos, n, &md5sig->head, node) {
                hlist_del_rcu(&key->node);
                atomic_sub(sizeof(*key), &sk->sk_omem_alloc);
                kfree_rcu(key, rcu);
        if (sin->sin_family != AF_INET)
                return -EINVAL;
 
-       if (!cmd.tcpm_key || !cmd.tcpm_keylen) {
-               if (!tcp_sk(sk)->md5sig_info)
-                       return -ENOENT;
+       if (!cmd.tcpm_key || !cmd.tcpm_keylen)
                return tcp_md5_do_del(sk, (union tcp_md5_addr *)&sin->sin_addr.s_addr,
                                      AF_INET);
-       }
 
        if (cmd.tcpm_keylen > TCP_MD5SIG_MAXKEYLEN)
                return -EINVAL;
        /* Clean up the MD5 key list, if any */
        if (tp->md5sig_info) {
                tcp_clear_md5_list(sk);
-               kfree(tp->md5sig_info);
+               kfree_rcu(tp->md5sig_info, rcu);
                tp->md5sig_info = NULL;
        }
 #endif