Commit e45cd8e1 authored by Eric Dumazet's avatar Eric Dumazet Committed by Luis Henriques

tcp: make sure skb is not shared before using skb_get()

commit ba34e6d9 upstream.

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: default avatarEric Dumazet <edumazet@google.com>
Signed-off-by: default avatarYuchung Cheng <ycheng@google.com>
Fixes: 5b7ed089 ("tcp: move fastopen functions to tcp_fastopen.c")
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
Signed-off-by: default avatarLuis Henriques <luis.henriques@canonical.com>
parent 21f90221
...@@ -134,6 +134,7 @@ static bool tcp_fastopen_create_child(struct sock *sk, ...@@ -134,6 +134,7 @@ static bool tcp_fastopen_create_child(struct sock *sk,
struct tcp_sock *tp; struct tcp_sock *tp;
struct request_sock_queue *queue = &inet_csk(sk)->icsk_accept_queue; struct request_sock_queue *queue = &inet_csk(sk)->icsk_accept_queue;
struct sock *child; struct sock *child;
u32 end_seq;
req->num_retrans = 0; req->num_retrans = 0;
req->num_timeout = 0; req->num_timeout = 0;
...@@ -185,20 +186,35 @@ static bool tcp_fastopen_create_child(struct sock *sk, ...@@ -185,20 +186,35 @@ static bool tcp_fastopen_create_child(struct sock *sk,
/* Queue the data carried in the SYN packet. We need to first /* Queue the data carried in the SYN packet. We need to first
* bump skb's refcnt because the caller will attempt to free it. * 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, * XXX (TFO) - we honor a zero-payload TFO request for now,
* (any reason not to?) but no need to queue the skb since * (any reason not to?) but no need to queue the skb since
* there is no data. How about SYN+FIN? * there is no data. How about SYN+FIN?
*/ */
if (TCP_SKB_CB(skb)->end_seq != TCP_SKB_CB(skb)->seq + 1) { end_seq = TCP_SKB_CB(skb)->end_seq;
skb = skb_get(skb); if (end_seq != TCP_SKB_CB(skb)->seq + 1) {
skb_dst_drop(skb); struct sk_buff *skb2;
__skb_pull(skb, tcp_hdr(skb)->doff * 4);
skb_set_owner_r(skb, child); if (unlikely(skb_shared(skb)))
__skb_queue_tail(&child->sk_receive_queue, 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; 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); sk->sk_data_ready(sk);
bh_unlock_sock(child); bh_unlock_sock(child);
sock_put(child); sock_put(child);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment