summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEric Dumazet <edumazet@google.com>2018-10-01 15:02:26 -0700
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2018-10-18 09:13:23 +0200
commit4cded0a3a9c69d3c08b2ab15a7dd8206775142e4 (patch)
tree40eacd693347a9f697f06e5402d474e0b2f8da89
parent069bb7fd6a27487e1ab6915faff1926464a8e275 (diff)
tcp/dccp: fix lockdep issue when SYN is backlogged
[ Upstream commit 1ad98e9d1bdf4724c0a8532fabd84bf3c457c2bc ] In normal SYN processing, packets are handled without listener lock and in RCU protected ingress path. But syzkaller is known to be able to trick us and SYN packets might be processed in process context, after being queued into socket backlog. In commit 06f877d613be ("tcp/dccp: fix other lockdep splats accessing ireq_opt") I made a very stupid fix, that happened to work mostly because of the regular path being RCU protected. Really the thing protecting ireq->ireq_opt is RCU read lock, and the pseudo request refcnt is not relevant. This patch extends what I did in commit 449809a66c1d ("tcp/dccp: block BH for SYN processing") by adding an extra rcu_read_{lock|unlock} pair in the paths that might be taken when processing SYN from socket backlog (thus possibly in process context) Fixes: 06f877d613be ("tcp/dccp: fix other lockdep splats accessing ireq_opt") Signed-off-by: Eric Dumazet <edumazet@google.com> Reported-by: syzbot <syzkaller@googlegroups.com> Signed-off-by: David S. Miller <davem@davemloft.net> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
-rw-r--r--include/net/inet_sock.h3
-rw-r--r--net/dccp/input.c4
-rw-r--r--net/ipv4/tcp_input.c4
3 files changed, 7 insertions, 4 deletions
diff --git a/include/net/inet_sock.h b/include/net/inet_sock.h
index 0464b207d0cf..02255bf8c4d2 100644
--- a/include/net/inet_sock.h
+++ b/include/net/inet_sock.h
@@ -134,8 +134,7 @@ static inline int inet_request_bound_dev_if(const struct sock *sk,
static inline struct ip_options_rcu *ireq_opt_deref(const struct inet_request_sock *ireq)
{
- return rcu_dereference_check(ireq->ireq_opt,
- atomic_read(&ireq->req.rsk_refcnt) > 0);
+ return rcu_dereference(ireq->ireq_opt);
}
struct inet_cork {
diff --git a/net/dccp/input.c b/net/dccp/input.c
index 4a05d7876850..84ff43acd427 100644
--- a/net/dccp/input.c
+++ b/net/dccp/input.c
@@ -605,11 +605,13 @@ int dccp_rcv_state_process(struct sock *sk, struct sk_buff *skb,
if (sk->sk_state == DCCP_LISTEN) {
if (dh->dccph_type == DCCP_PKT_REQUEST) {
/* It is possible that we process SYN packets from backlog,
- * so we need to make sure to disable BH right there.
+ * so we need to make sure to disable BH and RCU right there.
*/
+ rcu_read_lock();
local_bh_disable();
acceptable = inet_csk(sk)->icsk_af_ops->conn_request(sk, skb) >= 0;
local_bh_enable();
+ rcu_read_unlock();
if (!acceptable)
return 1;
consume_skb(skb);
diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c
index 9d0b73aa649f..3f8b5d3c1e59 100644
--- a/net/ipv4/tcp_input.c
+++ b/net/ipv4/tcp_input.c
@@ -5978,11 +5978,13 @@ int tcp_rcv_state_process(struct sock *sk, struct sk_buff *skb)
if (th->fin)
goto discard;
/* It is possible that we process SYN packets from backlog,
- * so we need to make sure to disable BH right there.
+ * so we need to make sure to disable BH and RCU right there.
*/
+ rcu_read_lock();
local_bh_disable();
acceptable = icsk->icsk_af_ops->conn_request(sk, skb) >= 0;
local_bh_enable();
+ rcu_read_unlock();
if (!acceptable)
return 1;