aboutsummaryrefslogtreecommitdiffstats
path: root/net/ipv6/inet6_connection_sock.c
diff options
context:
space:
mode:
authorEric Dumazet <edumazet@google.com>2015-11-29 22:37:57 -0500
committerDavid S. Miller <davem@davemloft.net>2015-12-02 23:37:16 -0500
commit45f6fad84cc305103b28d73482b344d7f5b76f39 (patch)
tree283dbc3a6cd4a26288a3526d0de48cf8c2e27b75 /net/ipv6/inet6_connection_sock.c
parent01b3f52157ff5a47d6d8d796f396a4b34a53c61d (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.c11
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}