Skip to content
This repository was archived by the owner on Jun 4, 2020. It is now read-only.

Commit d01d110

Browse files
Jon Maxwellgregkh
authored andcommitted
dccp/tcp: fix routing redirect race
commit 45caeaa5ac0b4b11784ac6f932c0ad4c6b67cda0 upstream. As Eric Dumazet pointed out this also needs to be fixed in IPv6. v2: Contains the IPv6 tcp/Ipv6 dccp patches as well. We have seen a few incidents lately where a dst_enty has been freed with a dangling TCP socket reference (sk->sk_dst_cache) pointing to that dst_entry. If the conditions/timings are right a crash then ensues when the freed dst_entry is referenced later on. A Common crashing back trace is: OnePlusOSS#8 [] page_fault at ffffffff8163e648 [exception RIP: __tcp_ack_snd_check+74] . . OnePlusOSS#9 [] tcp_rcv_established at ffffffff81580b64 OnePlusOSS#10 [] tcp_v4_do_rcv at ffffffff8158b54a OnePlusOSS#11 [] tcp_v4_rcv at ffffffff8158cd02 OnePlusOSS#12 [] ip_local_deliver_finish at ffffffff815668f4 OnePlusOSS#13 [] ip_local_deliver at ffffffff81566bd9 OnePlusOSS#14 [] ip_rcv_finish at ffffffff8156656d OnePlusOSS#15 [] ip_rcv at ffffffff81566f06 OnePlusOSS#16 [] __netif_receive_skb_core at ffffffff8152b3a2 OnePlusOSS#17 [] __netif_receive_skb at ffffffff8152b608 OnePlusOSS#18 [] netif_receive_skb at ffffffff8152b690 OnePlusOSS#19 [] vmxnet3_rq_rx_complete at ffffffffa015eeaf [vmxnet3] OnePlusOSS#20 [] vmxnet3_poll_rx_only at ffffffffa015f32a [vmxnet3] OnePlusOSS#21 [] net_rx_action at ffffffff8152bac2 OnePlusOSS#22 [] __do_softirq at ffffffff81084b4f OnePlusOSS#23 [] call_softirq at ffffffff8164845c OnePlusOSS#24 [] do_softirq at ffffffff81016fc5 OnePlusOSS#25 [] irq_exit at ffffffff81084ee5 OnePlusOSS#26 [] do_IRQ at ffffffff81648ff8 Of course it may happen with other NIC drivers as well. It's found the freed dst_entry here: 224 static bool tcp_in_quickack_mode(struct sock *sk)↩ 225 {↩ 226 ▹ const struct inet_connection_sock *icsk = inet_csk(sk);↩ 227 ▹ const struct dst_entry *dst = __sk_dst_get(sk);↩ 228 ↩ 229 ▹ return (dst && dst_metric(dst, RTAX_QUICKACK)) ||↩ 230 ▹ ▹ (icsk->icsk_ack.quick && !icsk->icsk_ack.pingpong);↩ 231 }↩ But there are other backtraces attributed to the same freed dst_entry in netfilter code as well. All the vmcores showed 2 significant clues: - Remote hosts behind the default gateway had always been redirected to a different gateway. A rtable/dst_entry will be added for that host. Making more dst_entrys with lower reference counts. Making this more probable. - All vmcores showed a postitive LockDroppedIcmps value, e.g: LockDroppedIcmps 267 A closer look at the tcp_v4_err() handler revealed that do_redirect() will run regardless of whether user space has the socket locked. This can result in a race condition where the same dst_entry cached in sk->sk_dst_entry can be decremented twice for the same socket via: do_redirect()->__sk_dst_check()-> dst_release(). Which leads to the dst_entry being prematurely freed with another socket pointing to it via sk->sk_dst_cache and a subsequent crash. To fix this skip do_redirect() if usespace has the socket locked. Instead let the redirect take place later when user space does not have the socket locked. The dccp/IPv6 code is very similar in this respect, so fixing it there too. As Eric Garver pointed out the following commit now invalidates routes. Which can set the dst->obsolete flag so that ipv4_dst_check() returns null and triggers the dst_release(). Fixes: ceb3320 ("ipv4: Kill routes during PMTU/redirect updates.") Cc: Eric Garver <egarver@redhat.com> Cc: Hannes Sowa <hsowa@redhat.com> Signed-off-by: Jon Maxwell <jmaxwell37@gmail.com> Signed-off-by: David S. Miller <davem@davemloft.net> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
1 parent 86812df commit d01d110

4 files changed

Lines changed: 14 additions & 8 deletions

File tree

net/dccp/ipv4.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -262,7 +262,8 @@ static void dccp_v4_err(struct sk_buff *skb, u32 info)
262262

263263
switch (type) {
264264
case ICMP_REDIRECT:
265-
dccp_do_redirect(skb, sk);
265+
if (!sock_owned_by_user(sk))
266+
dccp_do_redirect(skb, sk);
266267
goto out;
267268
case ICMP_SOURCE_QUENCH:
268269
/* Just silently ignore these. */

net/dccp/ipv6.c

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -131,10 +131,12 @@ static void dccp_v6_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
131131
np = inet6_sk(sk);
132132

133133
if (type == NDISC_REDIRECT) {
134-
struct dst_entry *dst = __sk_dst_check(sk, np->dst_cookie);
134+
if (!sock_owned_by_user(sk)) {
135+
struct dst_entry *dst = __sk_dst_check(sk, np->dst_cookie);
135136

136-
if (dst)
137-
dst->ops->redirect(dst, sk, skb);
137+
if (dst)
138+
dst->ops->redirect(dst, sk, skb);
139+
}
138140
goto out;
139141
}
140142

net/ipv4/tcp_ipv4.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -389,7 +389,8 @@ void tcp_v4_err(struct sk_buff *icmp_skb, u32 info)
389389

390390
switch (type) {
391391
case ICMP_REDIRECT:
392-
do_redirect(icmp_skb, sk);
392+
if (!sock_owned_by_user(sk))
393+
do_redirect(icmp_skb, sk);
393394
goto out;
394395
case ICMP_SOURCE_QUENCH:
395396
/* Just silently ignore these. */

net/ipv6/tcp_ipv6.c

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -389,10 +389,12 @@ static void tcp_v6_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
389389
np = inet6_sk(sk);
390390

391391
if (type == NDISC_REDIRECT) {
392-
struct dst_entry *dst = __sk_dst_check(sk, np->dst_cookie);
392+
if (!sock_owned_by_user(sk)) {
393+
struct dst_entry *dst = __sk_dst_check(sk, np->dst_cookie);
393394

394-
if (dst)
395-
dst->ops->redirect(dst, sk, skb);
395+
if (dst)
396+
dst->ops->redirect(dst, sk, skb);
397+
}
396398
goto out;
397399
}
398400

0 commit comments

Comments
 (0)