tcp: md5: fix rcu lockdep splat
authorEric Dumazet <edumazet@google.com>
Tue, 24 Mar 2015 22:58:52 +0000 (15:58 -0700)
committerDavid S. Miller <davem@davemloft.net>
Wed, 25 Mar 2015 01:16:29 +0000 (21:16 -0400)
While timer handler effectively runs a rcu read locked section,
there is no explicit rcu_read_lock()/rcu_read_unlock() annotations
and lockdep can be confused here :

net/ipv4/tcp_ipv4.c-906-        /* caller either holds rcu_read_lock() or socket lock */
net/ipv4/tcp_ipv4.c:907:        md5sig = rcu_dereference_check(tp->md5sig_info,
net/ipv4/tcp_ipv4.c-908-                                       sock_owned_by_user(sk) ||
net/ipv4/tcp_ipv4.c-909-                                       lockdep_is_held(&sk->sk_lock.slock));

Let's explicitely acquire rcu_read_lock() in tcp_make_synack()

Before commit fa76ce7328b ("inet: get rid of central tcp/dccp listener
timer"), we were holding listener lock so lockdep was happy.

Fixes: fa76ce7328b ("inet: get rid of central tcp/dccp listener timer")
Signed-off-by: Eric DUmazet <edumazet@google.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
net/ipv4/tcp_output.c

index 18474088c3d095c8675f5f3607b1f1482dc5f447..5b7fad4b314c13e73812dd240c1ee9428b857133 100644 (file)
@@ -601,15 +601,14 @@ static unsigned int tcp_synack_options(struct sock *sk,
                                   struct request_sock *req,
                                   unsigned int mss, struct sk_buff *skb,
                                   struct tcp_out_options *opts,
-                                  struct tcp_md5sig_key **md5,
+                                  const struct tcp_md5sig_key *md5,
                                   struct tcp_fastopen_cookie *foc)
 {
        struct inet_request_sock *ireq = inet_rsk(req);
        unsigned int remaining = MAX_TCP_OPTION_SPACE;
 
 #ifdef CONFIG_TCP_MD5SIG
-       *md5 = tcp_rsk(req)->af_specific->md5_lookup(sk, req);
-       if (*md5) {
+       if (md5) {
                opts->options |= OPTION_MD5;
                remaining -= TCPOLEN_MD5SIG_ALIGNED;
 
@@ -620,8 +619,6 @@ static unsigned int tcp_synack_options(struct sock *sk,
                 */
                ireq->tstamp_ok &= !ireq->sack_ok;
        }
-#else
-       *md5 = NULL;
 #endif
 
        /* We always send an MSS option. */
@@ -2913,7 +2910,7 @@ struct sk_buff *tcp_make_synack(struct sock *sk, struct dst_entry *dst,
        struct tcp_sock *tp = tcp_sk(sk);
        struct tcphdr *th;
        struct sk_buff *skb;
-       struct tcp_md5sig_key *md5;
+       struct tcp_md5sig_key *md5 = NULL;
        int tcp_header_size;
        int mss;
 
@@ -2938,7 +2935,12 @@ struct sk_buff *tcp_make_synack(struct sock *sk, struct dst_entry *dst,
        else
 #endif
        skb_mstamp_get(&skb->skb_mstamp);
-       tcp_header_size = tcp_synack_options(sk, req, mss, skb, &opts, &md5,
+
+#ifdef CONFIG_TCP_MD5SIG
+       rcu_read_lock();
+       md5 = tcp_rsk(req)->af_specific->md5_lookup(sk, req);
+#endif
+       tcp_header_size = tcp_synack_options(sk, req, mss, skb, &opts, md5,
                                             foc) + sizeof(*th);
 
        skb_push(skb, tcp_header_size);
@@ -2969,10 +2971,10 @@ struct sk_buff *tcp_make_synack(struct sock *sk, struct dst_entry *dst,
 
 #ifdef CONFIG_TCP_MD5SIG
        /* Okay, we have all we need - do the md5 hash if needed */
-       if (md5) {
+       if (md5)
                tcp_rsk(req)->af_specific->calc_md5_hash(opts.hash_location,
                                               md5, NULL, req, skb);
-       }
+       rcu_read_unlock();
 #endif
 
        return skb;