diff options
author | Eric Dumazet <edumazet@google.com> | 2015-11-29 22:37:57 -0500 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2015-12-02 23:37:16 -0500 |
commit | 45f6fad84cc305103b28d73482b344d7f5b76f39 (patch) | |
tree | 283dbc3a6cd4a26288a3526d0de48cf8c2e27b75 /net/ipv6/inet6_connection_sock.c | |
parent | 01b3f52157ff5a47d6d8d796f396a4b34a53c61d (diff) |
ipv6: add complete rcu protection around np->opt
This patch addresses multiple problems :
UDP/RAW sendmsg() need to get a stable struct ipv6_txoptions
while socket is not locked : Other threads can change np->opt
concurrently. Dmitry posted a syzkaller
(http://github.com/google/syzkaller) program desmonstrating
use-after-free.
Starting with TCP/DCCP lockless listeners, tcp_v6_syn_recv_sock()
and dccp_v6_request_recv_sock() also need to use RCU protection
to dereference np->opt once (before calling ipv6_dup_options())
This patch adds full RCU protection to np->opt
Reported-by: Dmitry Vyukov <dvyukov@google.com>
Signed-off-by: Eric Dumazet <edumazet@google.com>
Acked-by: Hannes Frederic Sowa <hannes@stressinduktion.org>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/ipv6/inet6_connection_sock.c')
-rw-r--r-- | net/ipv6/inet6_connection_sock.c | 11 |
1 files changed, 8 insertions, 3 deletions
diff --git a/net/ipv6/inet6_connection_sock.c b/net/ipv6/inet6_connection_sock.c index 5d1c7cee2cb2..3ff5208772bb 100644 --- a/net/ipv6/inet6_connection_sock.c +++ b/net/ipv6/inet6_connection_sock.c | |||
@@ -78,7 +78,9 @@ struct dst_entry *inet6_csk_route_req(const struct sock *sk, | |||
78 | memset(fl6, 0, sizeof(*fl6)); | 78 | memset(fl6, 0, sizeof(*fl6)); |
79 | fl6->flowi6_proto = proto; | 79 | fl6->flowi6_proto = proto; |
80 | fl6->daddr = ireq->ir_v6_rmt_addr; | 80 | fl6->daddr = ireq->ir_v6_rmt_addr; |
81 | final_p = fl6_update_dst(fl6, np->opt, &final); | 81 | rcu_read_lock(); |
82 | final_p = fl6_update_dst(fl6, rcu_dereference(np->opt), &final); | ||
83 | rcu_read_unlock(); | ||
82 | fl6->saddr = ireq->ir_v6_loc_addr; | 84 | fl6->saddr = ireq->ir_v6_loc_addr; |
83 | fl6->flowi6_oif = ireq->ir_iif; | 85 | fl6->flowi6_oif = ireq->ir_iif; |
84 | fl6->flowi6_mark = ireq->ir_mark; | 86 | fl6->flowi6_mark = ireq->ir_mark; |
@@ -142,7 +144,9 @@ static struct dst_entry *inet6_csk_route_socket(struct sock *sk, | |||
142 | fl6->fl6_dport = inet->inet_dport; | 144 | fl6->fl6_dport = inet->inet_dport; |
143 | security_sk_classify_flow(sk, flowi6_to_flowi(fl6)); | 145 | security_sk_classify_flow(sk, flowi6_to_flowi(fl6)); |
144 | 146 | ||
145 | final_p = fl6_update_dst(fl6, np->opt, &final); | 147 | rcu_read_lock(); |
148 | final_p = fl6_update_dst(fl6, rcu_dereference(np->opt), &final); | ||
149 | rcu_read_unlock(); | ||
146 | 150 | ||
147 | dst = __inet6_csk_dst_check(sk, np->dst_cookie); | 151 | dst = __inet6_csk_dst_check(sk, np->dst_cookie); |
148 | if (!dst) { | 152 | if (!dst) { |
@@ -175,7 +179,8 @@ int inet6_csk_xmit(struct sock *sk, struct sk_buff *skb, struct flowi *fl_unused | |||
175 | /* Restore final destination back after routing done */ | 179 | /* Restore final destination back after routing done */ |
176 | fl6.daddr = sk->sk_v6_daddr; | 180 | fl6.daddr = sk->sk_v6_daddr; |
177 | 181 | ||
178 | res = ip6_xmit(sk, skb, &fl6, np->opt, np->tclass); | 182 | res = ip6_xmit(sk, skb, &fl6, rcu_dereference(np->opt), |
183 | np->tclass); | ||
179 | rcu_read_unlock(); | 184 | rcu_read_unlock(); |
180 | return res; | 185 | return res; |
181 | } | 186 | } |