3.19-stable review patch.  If anyone has any objections, please let me know.

------------------

From: Eric Dumazet <eduma...@google.com>

[ Upstream commit ba34e6d9d346fe4e05d7e417b9edf5140772d34c ]

IPv6 can keep a copy of SYN message using skb_get() in
tcp_v6_conn_request() so that caller wont free the skb when calling
kfree_skb() later.

Therefore TCP fast open has to clone the skb it is queuing in
child->sk_receive_queue, as all skbs consumed from receive_queue are
freed using __kfree_skb() (ie assuming skb->users == 1)

Signed-off-by: Eric Dumazet <eduma...@google.com>
Signed-off-by: Yuchung Cheng <ych...@google.com>
Fixes: 5b7ed0892f2af ("tcp: move fastopen functions to tcp_fastopen.c")
Signed-off-by: David S. Miller <da...@davemloft.net>
Signed-off-by: Greg Kroah-Hartman <gre...@linuxfoundation.org>
---
 net/ipv4/tcp_fastopen.c |   32 ++++++++++++++++++++++++--------
 1 file changed, 24 insertions(+), 8 deletions(-)

--- a/net/ipv4/tcp_fastopen.c
+++ b/net/ipv4/tcp_fastopen.c
@@ -134,6 +134,7 @@ static bool tcp_fastopen_create_child(st
        struct tcp_sock *tp;
        struct request_sock_queue *queue = &inet_csk(sk)->icsk_accept_queue;
        struct sock *child;
+       u32 end_seq;
 
        req->num_retrans = 0;
        req->num_timeout = 0;
@@ -185,20 +186,35 @@ static bool tcp_fastopen_create_child(st
 
        /* Queue the data carried in the SYN packet. We need to first
         * bump skb's refcnt because the caller will attempt to free it.
+        * Note that IPv6 might also have used skb_get() trick
+        * in tcp_v6_conn_request() to keep this SYN around (treq->pktopts)
+        * So we need to eventually get a clone of the packet,
+        * before inserting it in sk_receive_queue.
         *
         * XXX (TFO) - we honor a zero-payload TFO request for now,
         * (any reason not to?) but no need to queue the skb since
         * there is no data. How about SYN+FIN?
         */
-       if (TCP_SKB_CB(skb)->end_seq != TCP_SKB_CB(skb)->seq + 1) {
-               skb = skb_get(skb);
-               skb_dst_drop(skb);
-               __skb_pull(skb, tcp_hdr(skb)->doff * 4);
-               skb_set_owner_r(skb, child);
-               __skb_queue_tail(&child->sk_receive_queue, skb);
-               tp->syn_data_acked = 1;
+       end_seq = TCP_SKB_CB(skb)->end_seq;
+       if (end_seq != TCP_SKB_CB(skb)->seq + 1) {
+               struct sk_buff *skb2;
+
+               if (unlikely(skb_shared(skb)))
+                       skb2 = skb_clone(skb, GFP_ATOMIC);
+               else
+                       skb2 = skb_get(skb);
+
+               if (likely(skb2)) {
+                       skb_dst_drop(skb2);
+                       __skb_pull(skb2, tcp_hdrlen(skb));
+                       skb_set_owner_r(skb2, child);
+                       __skb_queue_tail(&child->sk_receive_queue, skb2);
+                       tp->syn_data_acked = 1;
+               } else {
+                       end_seq = TCP_SKB_CB(skb)->seq + 1;
+               }
        }
-       tcp_rsk(req)->rcv_nxt = tp->rcv_nxt = TCP_SKB_CB(skb)->end_seq;
+       tcp_rsk(req)->rcv_nxt = tp->rcv_nxt = end_seq;
        sk->sk_data_ready(sk);
        bh_unlock_sock(child);
        sock_put(child);


--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

Reply via email to