aboutsummaryrefslogtreecommitdiffstats
path: root/net/ipv4/tcp_minisocks.c
diff options
context:
space:
mode:
authorEric Dumazet <edumazet@google.com>2015-10-22 11:20:46 -0400
committerDavid S. Miller <davem@davemloft.net>2015-10-23 08:42:21 -0400
commit5e0724d027f0548511a2165a209572d48fe7a4c8 (patch)
tree709301be9b56652004047b89f4467b3c917814cd /net/ipv4/tcp_minisocks.c
parent7b1311807f3d3eb8bef3ccc53127838b3bea3771 (diff)
tcp/dccp: fix hashdance race for passive sessions
Multiple cpus can process duplicates of incoming ACK messages matching a SYN_RECV request socket. This is a rare event under normal operations, but definitely can happen. Only one must win the race, otherwise corruption would occur. To fix this without adding new atomic ops, we use logic in inet_ehash_nolisten() to detect the request was present in the same ehash bucket where we try to insert the new child. If request socket was not found, we have to undo the child creation. This actually removes a spin_lock()/spin_unlock() pair in reqsk_queue_unlink() for the fast path. Fixes: e994b2f0fb92 ("tcp: do not lock listener to process SYN packets") Fixes: 079096f103fa ("tcp/dccp: install syn_recv requests into ehash table") Signed-off-by: Eric Dumazet <edumazet@google.com> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/ipv4/tcp_minisocks.c')
-rw-r--r--net/ipv4/tcp_minisocks.c11
1 files changed, 4 insertions, 7 deletions
diff --git a/net/ipv4/tcp_minisocks.c b/net/ipv4/tcp_minisocks.c
index 1fd5d413a664..3575dd1e5b67 100644
--- a/net/ipv4/tcp_minisocks.c
+++ b/net/ipv4/tcp_minisocks.c
@@ -580,6 +580,7 @@ struct sock *tcp_check_req(struct sock *sk, struct sk_buff *skb,
580 const struct tcphdr *th = tcp_hdr(skb); 580 const struct tcphdr *th = tcp_hdr(skb);
581 __be32 flg = tcp_flag_word(th) & (TCP_FLAG_RST|TCP_FLAG_SYN|TCP_FLAG_ACK); 581 __be32 flg = tcp_flag_word(th) & (TCP_FLAG_RST|TCP_FLAG_SYN|TCP_FLAG_ACK);
582 bool paws_reject = false; 582 bool paws_reject = false;
583 bool own_req;
583 584
584 tmp_opt.saw_tstamp = 0; 585 tmp_opt.saw_tstamp = 0;
585 if (th->doff > (sizeof(struct tcphdr)>>2)) { 586 if (th->doff > (sizeof(struct tcphdr)>>2)) {
@@ -767,18 +768,14 @@ struct sock *tcp_check_req(struct sock *sk, struct sk_buff *skb,
767 * ESTABLISHED STATE. If it will be dropped after 768 * ESTABLISHED STATE. If it will be dropped after
768 * socket is created, wait for troubles. 769 * socket is created, wait for troubles.
769 */ 770 */
770 child = inet_csk(sk)->icsk_af_ops->syn_recv_sock(sk, skb, req, NULL); 771 child = inet_csk(sk)->icsk_af_ops->syn_recv_sock(sk, skb, req, NULL,
772 req, &own_req);
771 if (!child) 773 if (!child)
772 goto listen_overflow; 774 goto listen_overflow;
773 775
774 sock_rps_save_rxhash(child, skb); 776 sock_rps_save_rxhash(child, skb);
775 tcp_synack_rtt_meas(child, req); 777 tcp_synack_rtt_meas(child, req);
776 inet_csk_reqsk_queue_drop(sk, req); 778 return inet_csk_complete_hashdance(sk, child, req, own_req);
777 inet_csk_reqsk_queue_add(sk, req, child);
778 /* Warning: caller must not call reqsk_put(req);
779 * child stole last reference on it.
780 */
781 return child;
782 779
783listen_overflow: 780listen_overflow:
784 if (!sysctl_tcp_abort_on_overflow) { 781 if (!sysctl_tcp_abort_on_overflow) {