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 | |
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')
-rw-r--r-- | net/dccp/ipv6.c | 33 | ||||
-rw-r--r-- | net/ipv6/af_inet6.c | 13 | ||||
-rw-r--r-- | net/ipv6/datagram.c | 4 | ||||
-rw-r--r-- | net/ipv6/exthdrs.c | 3 | ||||
-rw-r--r-- | net/ipv6/inet6_connection_sock.c | 11 | ||||
-rw-r--r-- | net/ipv6/ipv6_sockglue.c | 33 | ||||
-rw-r--r-- | net/ipv6/raw.c | 8 | ||||
-rw-r--r-- | net/ipv6/syncookies.c | 2 | ||||
-rw-r--r-- | net/ipv6/tcp_ipv6.c | 28 | ||||
-rw-r--r-- | net/ipv6/udp.c | 8 | ||||
-rw-r--r-- | net/l2tp/l2tp_ip6.c | 8 |
11 files changed, 101 insertions, 50 deletions
diff --git a/net/dccp/ipv6.c b/net/dccp/ipv6.c index db5fc2440a23..e7e0b9bc2a43 100644 --- a/net/dccp/ipv6.c +++ b/net/dccp/ipv6.c | |||
@@ -202,7 +202,9 @@ static int dccp_v6_send_response(const struct sock *sk, struct request_sock *req | |||
202 | security_req_classify_flow(req, flowi6_to_flowi(&fl6)); | 202 | security_req_classify_flow(req, flowi6_to_flowi(&fl6)); |
203 | 203 | ||
204 | 204 | ||
205 | final_p = fl6_update_dst(&fl6, np->opt, &final); | 205 | rcu_read_lock(); |
206 | final_p = fl6_update_dst(&fl6, rcu_dereference(np->opt), &final); | ||
207 | rcu_read_unlock(); | ||
206 | 208 | ||
207 | dst = ip6_dst_lookup_flow(sk, &fl6, final_p); | 209 | dst = ip6_dst_lookup_flow(sk, &fl6, final_p); |
208 | if (IS_ERR(dst)) { | 210 | if (IS_ERR(dst)) { |
@@ -219,7 +221,10 @@ static int dccp_v6_send_response(const struct sock *sk, struct request_sock *req | |||
219 | &ireq->ir_v6_loc_addr, | 221 | &ireq->ir_v6_loc_addr, |
220 | &ireq->ir_v6_rmt_addr); | 222 | &ireq->ir_v6_rmt_addr); |
221 | fl6.daddr = ireq->ir_v6_rmt_addr; | 223 | fl6.daddr = ireq->ir_v6_rmt_addr; |
222 | err = ip6_xmit(sk, skb, &fl6, np->opt, np->tclass); | 224 | rcu_read_lock(); |
225 | err = ip6_xmit(sk, skb, &fl6, rcu_dereference(np->opt), | ||
226 | np->tclass); | ||
227 | rcu_read_unlock(); | ||
223 | err = net_xmit_eval(err); | 228 | err = net_xmit_eval(err); |
224 | } | 229 | } |
225 | 230 | ||
@@ -387,6 +392,7 @@ static struct sock *dccp_v6_request_recv_sock(const struct sock *sk, | |||
387 | struct inet_request_sock *ireq = inet_rsk(req); | 392 | struct inet_request_sock *ireq = inet_rsk(req); |
388 | struct ipv6_pinfo *newnp; | 393 | struct ipv6_pinfo *newnp; |
389 | const struct ipv6_pinfo *np = inet6_sk(sk); | 394 | const struct ipv6_pinfo *np = inet6_sk(sk); |
395 | struct ipv6_txoptions *opt; | ||
390 | struct inet_sock *newinet; | 396 | struct inet_sock *newinet; |
391 | struct dccp6_sock *newdp6; | 397 | struct dccp6_sock *newdp6; |
392 | struct sock *newsk; | 398 | struct sock *newsk; |
@@ -488,13 +494,15 @@ static struct sock *dccp_v6_request_recv_sock(const struct sock *sk, | |||
488 | * Yes, keeping reference count would be much more clever, but we make | 494 | * Yes, keeping reference count would be much more clever, but we make |
489 | * one more one thing there: reattach optmem to newsk. | 495 | * one more one thing there: reattach optmem to newsk. |
490 | */ | 496 | */ |
491 | if (np->opt != NULL) | 497 | opt = rcu_dereference(np->opt); |
492 | newnp->opt = ipv6_dup_options(newsk, np->opt); | 498 | if (opt) { |
493 | 499 | opt = ipv6_dup_options(newsk, opt); | |
500 | RCU_INIT_POINTER(newnp->opt, opt); | ||
501 | } | ||
494 | inet_csk(newsk)->icsk_ext_hdr_len = 0; | 502 | inet_csk(newsk)->icsk_ext_hdr_len = 0; |
495 | if (newnp->opt != NULL) | 503 | if (opt) |
496 | inet_csk(newsk)->icsk_ext_hdr_len = (newnp->opt->opt_nflen + | 504 | inet_csk(newsk)->icsk_ext_hdr_len = opt->opt_nflen + |
497 | newnp->opt->opt_flen); | 505 | opt->opt_flen; |
498 | 506 | ||
499 | dccp_sync_mss(newsk, dst_mtu(dst)); | 507 | dccp_sync_mss(newsk, dst_mtu(dst)); |
500 | 508 | ||
@@ -757,6 +765,7 @@ static int dccp_v6_connect(struct sock *sk, struct sockaddr *uaddr, | |||
757 | struct ipv6_pinfo *np = inet6_sk(sk); | 765 | struct ipv6_pinfo *np = inet6_sk(sk); |
758 | struct dccp_sock *dp = dccp_sk(sk); | 766 | struct dccp_sock *dp = dccp_sk(sk); |
759 | struct in6_addr *saddr = NULL, *final_p, final; | 767 | struct in6_addr *saddr = NULL, *final_p, final; |
768 | struct ipv6_txoptions *opt; | ||
760 | struct flowi6 fl6; | 769 | struct flowi6 fl6; |
761 | struct dst_entry *dst; | 770 | struct dst_entry *dst; |
762 | int addr_type; | 771 | int addr_type; |
@@ -856,7 +865,8 @@ static int dccp_v6_connect(struct sock *sk, struct sockaddr *uaddr, | |||
856 | fl6.fl6_sport = inet->inet_sport; | 865 | fl6.fl6_sport = inet->inet_sport; |
857 | security_sk_classify_flow(sk, flowi6_to_flowi(&fl6)); | 866 | security_sk_classify_flow(sk, flowi6_to_flowi(&fl6)); |
858 | 867 | ||
859 | final_p = fl6_update_dst(&fl6, np->opt, &final); | 868 | opt = rcu_dereference_protected(np->opt, sock_owned_by_user(sk)); |
869 | final_p = fl6_update_dst(&fl6, opt, &final); | ||
860 | 870 | ||
861 | dst = ip6_dst_lookup_flow(sk, &fl6, final_p); | 871 | dst = ip6_dst_lookup_flow(sk, &fl6, final_p); |
862 | if (IS_ERR(dst)) { | 872 | if (IS_ERR(dst)) { |
@@ -876,9 +886,8 @@ static int dccp_v6_connect(struct sock *sk, struct sockaddr *uaddr, | |||
876 | __ip6_dst_store(sk, dst, NULL, NULL); | 886 | __ip6_dst_store(sk, dst, NULL, NULL); |
877 | 887 | ||
878 | icsk->icsk_ext_hdr_len = 0; | 888 | icsk->icsk_ext_hdr_len = 0; |
879 | if (np->opt != NULL) | 889 | if (opt) |
880 | icsk->icsk_ext_hdr_len = (np->opt->opt_flen + | 890 | icsk->icsk_ext_hdr_len = opt->opt_flen + opt->opt_nflen; |
881 | np->opt->opt_nflen); | ||
882 | 891 | ||
883 | inet->inet_dport = usin->sin6_port; | 892 | inet->inet_dport = usin->sin6_port; |
884 | 893 | ||
diff --git a/net/ipv6/af_inet6.c b/net/ipv6/af_inet6.c index 44bb66bde0e2..38d66ddfb937 100644 --- a/net/ipv6/af_inet6.c +++ b/net/ipv6/af_inet6.c | |||
@@ -428,9 +428,11 @@ void inet6_destroy_sock(struct sock *sk) | |||
428 | 428 | ||
429 | /* Free tx options */ | 429 | /* Free tx options */ |
430 | 430 | ||
431 | opt = xchg(&np->opt, NULL); | 431 | opt = xchg((__force struct ipv6_txoptions **)&np->opt, NULL); |
432 | if (opt) | 432 | if (opt) { |
433 | sock_kfree_s(sk, opt, opt->tot_len); | 433 | atomic_sub(opt->tot_len, &sk->sk_omem_alloc); |
434 | txopt_put(opt); | ||
435 | } | ||
434 | } | 436 | } |
435 | EXPORT_SYMBOL_GPL(inet6_destroy_sock); | 437 | EXPORT_SYMBOL_GPL(inet6_destroy_sock); |
436 | 438 | ||
@@ -659,7 +661,10 @@ int inet6_sk_rebuild_header(struct sock *sk) | |||
659 | fl6.fl6_sport = inet->inet_sport; | 661 | fl6.fl6_sport = inet->inet_sport; |
660 | security_sk_classify_flow(sk, flowi6_to_flowi(&fl6)); | 662 | security_sk_classify_flow(sk, flowi6_to_flowi(&fl6)); |
661 | 663 | ||
662 | final_p = fl6_update_dst(&fl6, np->opt, &final); | 664 | rcu_read_lock(); |
665 | final_p = fl6_update_dst(&fl6, rcu_dereference(np->opt), | ||
666 | &final); | ||
667 | rcu_read_unlock(); | ||
663 | 668 | ||
664 | dst = ip6_dst_lookup_flow(sk, &fl6, final_p); | 669 | dst = ip6_dst_lookup_flow(sk, &fl6, final_p); |
665 | if (IS_ERR(dst)) { | 670 | if (IS_ERR(dst)) { |
diff --git a/net/ipv6/datagram.c b/net/ipv6/datagram.c index d70b0238f468..517c55b01ba8 100644 --- a/net/ipv6/datagram.c +++ b/net/ipv6/datagram.c | |||
@@ -167,8 +167,10 @@ ipv4_connected: | |||
167 | 167 | ||
168 | security_sk_classify_flow(sk, flowi6_to_flowi(&fl6)); | 168 | security_sk_classify_flow(sk, flowi6_to_flowi(&fl6)); |
169 | 169 | ||
170 | opt = flowlabel ? flowlabel->opt : np->opt; | 170 | rcu_read_lock(); |
171 | opt = flowlabel ? flowlabel->opt : rcu_dereference(np->opt); | ||
171 | final_p = fl6_update_dst(&fl6, opt, &final); | 172 | final_p = fl6_update_dst(&fl6, opt, &final); |
173 | rcu_read_unlock(); | ||
172 | 174 | ||
173 | dst = ip6_dst_lookup_flow(sk, &fl6, final_p); | 175 | dst = ip6_dst_lookup_flow(sk, &fl6, final_p); |
174 | err = 0; | 176 | err = 0; |
diff --git a/net/ipv6/exthdrs.c b/net/ipv6/exthdrs.c index ce203b0402be..ea7c4d64a00a 100644 --- a/net/ipv6/exthdrs.c +++ b/net/ipv6/exthdrs.c | |||
@@ -727,6 +727,7 @@ ipv6_dup_options(struct sock *sk, struct ipv6_txoptions *opt) | |||
727 | *((char **)&opt2->dst1opt) += dif; | 727 | *((char **)&opt2->dst1opt) += dif; |
728 | if (opt2->srcrt) | 728 | if (opt2->srcrt) |
729 | *((char **)&opt2->srcrt) += dif; | 729 | *((char **)&opt2->srcrt) += dif; |
730 | atomic_set(&opt2->refcnt, 1); | ||
730 | } | 731 | } |
731 | return opt2; | 732 | return opt2; |
732 | } | 733 | } |
@@ -790,7 +791,7 @@ ipv6_renew_options(struct sock *sk, struct ipv6_txoptions *opt, | |||
790 | return ERR_PTR(-ENOBUFS); | 791 | return ERR_PTR(-ENOBUFS); |
791 | 792 | ||
792 | memset(opt2, 0, tot_len); | 793 | memset(opt2, 0, tot_len); |
793 | 794 | atomic_set(&opt2->refcnt, 1); | |
794 | opt2->tot_len = tot_len; | 795 | opt2->tot_len = tot_len; |
795 | p = (char *)(opt2 + 1); | 796 | p = (char *)(opt2 + 1); |
796 | 797 | ||
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 | } |
diff --git a/net/ipv6/ipv6_sockglue.c b/net/ipv6/ipv6_sockglue.c index 63e6956917c9..4449ad1f8114 100644 --- a/net/ipv6/ipv6_sockglue.c +++ b/net/ipv6/ipv6_sockglue.c | |||
@@ -111,7 +111,8 @@ struct ipv6_txoptions *ipv6_update_options(struct sock *sk, | |||
111 | icsk->icsk_sync_mss(sk, icsk->icsk_pmtu_cookie); | 111 | icsk->icsk_sync_mss(sk, icsk->icsk_pmtu_cookie); |
112 | } | 112 | } |
113 | } | 113 | } |
114 | opt = xchg(&inet6_sk(sk)->opt, opt); | 114 | opt = xchg((__force struct ipv6_txoptions **)&inet6_sk(sk)->opt, |
115 | opt); | ||
115 | sk_dst_reset(sk); | 116 | sk_dst_reset(sk); |
116 | 117 | ||
117 | return opt; | 118 | return opt; |
@@ -231,9 +232,12 @@ static int do_ipv6_setsockopt(struct sock *sk, int level, int optname, | |||
231 | sk->sk_socket->ops = &inet_dgram_ops; | 232 | sk->sk_socket->ops = &inet_dgram_ops; |
232 | sk->sk_family = PF_INET; | 233 | sk->sk_family = PF_INET; |
233 | } | 234 | } |
234 | opt = xchg(&np->opt, NULL); | 235 | opt = xchg((__force struct ipv6_txoptions **)&np->opt, |
235 | if (opt) | 236 | NULL); |
236 | sock_kfree_s(sk, opt, opt->tot_len); | 237 | if (opt) { |
238 | atomic_sub(opt->tot_len, &sk->sk_omem_alloc); | ||
239 | txopt_put(opt); | ||
240 | } | ||
237 | pktopt = xchg(&np->pktoptions, NULL); | 241 | pktopt = xchg(&np->pktoptions, NULL); |
238 | kfree_skb(pktopt); | 242 | kfree_skb(pktopt); |
239 | 243 | ||
@@ -403,7 +407,8 @@ static int do_ipv6_setsockopt(struct sock *sk, int level, int optname, | |||
403 | if (optname != IPV6_RTHDR && !ns_capable(net->user_ns, CAP_NET_RAW)) | 407 | if (optname != IPV6_RTHDR && !ns_capable(net->user_ns, CAP_NET_RAW)) |
404 | break; | 408 | break; |
405 | 409 | ||
406 | opt = ipv6_renew_options(sk, np->opt, optname, | 410 | opt = rcu_dereference_protected(np->opt, sock_owned_by_user(sk)); |
411 | opt = ipv6_renew_options(sk, opt, optname, | ||
407 | (struct ipv6_opt_hdr __user *)optval, | 412 | (struct ipv6_opt_hdr __user *)optval, |
408 | optlen); | 413 | optlen); |
409 | if (IS_ERR(opt)) { | 414 | if (IS_ERR(opt)) { |
@@ -432,8 +437,10 @@ static int do_ipv6_setsockopt(struct sock *sk, int level, int optname, | |||
432 | retv = 0; | 437 | retv = 0; |
433 | opt = ipv6_update_options(sk, opt); | 438 | opt = ipv6_update_options(sk, opt); |
434 | sticky_done: | 439 | sticky_done: |
435 | if (opt) | 440 | if (opt) { |
436 | sock_kfree_s(sk, opt, opt->tot_len); | 441 | atomic_sub(opt->tot_len, &sk->sk_omem_alloc); |
442 | txopt_put(opt); | ||
443 | } | ||
437 | break; | 444 | break; |
438 | } | 445 | } |
439 | 446 | ||
@@ -486,6 +493,7 @@ sticky_done: | |||
486 | break; | 493 | break; |
487 | 494 | ||
488 | memset(opt, 0, sizeof(*opt)); | 495 | memset(opt, 0, sizeof(*opt)); |
496 | atomic_set(&opt->refcnt, 1); | ||
489 | opt->tot_len = sizeof(*opt) + optlen; | 497 | opt->tot_len = sizeof(*opt) + optlen; |
490 | retv = -EFAULT; | 498 | retv = -EFAULT; |
491 | if (copy_from_user(opt+1, optval, optlen)) | 499 | if (copy_from_user(opt+1, optval, optlen)) |
@@ -502,8 +510,10 @@ update: | |||
502 | retv = 0; | 510 | retv = 0; |
503 | opt = ipv6_update_options(sk, opt); | 511 | opt = ipv6_update_options(sk, opt); |
504 | done: | 512 | done: |
505 | if (opt) | 513 | if (opt) { |
506 | sock_kfree_s(sk, opt, opt->tot_len); | 514 | atomic_sub(opt->tot_len, &sk->sk_omem_alloc); |
515 | txopt_put(opt); | ||
516 | } | ||
507 | break; | 517 | break; |
508 | } | 518 | } |
509 | case IPV6_UNICAST_HOPS: | 519 | case IPV6_UNICAST_HOPS: |
@@ -1110,10 +1120,11 @@ static int do_ipv6_getsockopt(struct sock *sk, int level, int optname, | |||
1110 | case IPV6_RTHDR: | 1120 | case IPV6_RTHDR: |
1111 | case IPV6_DSTOPTS: | 1121 | case IPV6_DSTOPTS: |
1112 | { | 1122 | { |
1123 | struct ipv6_txoptions *opt; | ||
1113 | 1124 | ||
1114 | lock_sock(sk); | 1125 | lock_sock(sk); |
1115 | len = ipv6_getsockopt_sticky(sk, np->opt, | 1126 | opt = rcu_dereference_protected(np->opt, sock_owned_by_user(sk)); |
1116 | optname, optval, len); | 1127 | len = ipv6_getsockopt_sticky(sk, opt, optname, optval, len); |
1117 | release_sock(sk); | 1128 | release_sock(sk); |
1118 | /* check if ipv6_getsockopt_sticky() returns err code */ | 1129 | /* check if ipv6_getsockopt_sticky() returns err code */ |
1119 | if (len < 0) | 1130 | if (len < 0) |
diff --git a/net/ipv6/raw.c b/net/ipv6/raw.c index dc65ec198f7c..99140986e887 100644 --- a/net/ipv6/raw.c +++ b/net/ipv6/raw.c | |||
@@ -733,6 +733,7 @@ static int raw6_getfrag(void *from, char *to, int offset, int len, int odd, | |||
733 | 733 | ||
734 | static int rawv6_sendmsg(struct sock *sk, struct msghdr *msg, size_t len) | 734 | static int rawv6_sendmsg(struct sock *sk, struct msghdr *msg, size_t len) |
735 | { | 735 | { |
736 | struct ipv6_txoptions *opt_to_free = NULL; | ||
736 | struct ipv6_txoptions opt_space; | 737 | struct ipv6_txoptions opt_space; |
737 | DECLARE_SOCKADDR(struct sockaddr_in6 *, sin6, msg->msg_name); | 738 | DECLARE_SOCKADDR(struct sockaddr_in6 *, sin6, msg->msg_name); |
738 | struct in6_addr *daddr, *final_p, final; | 739 | struct in6_addr *daddr, *final_p, final; |
@@ -839,8 +840,10 @@ static int rawv6_sendmsg(struct sock *sk, struct msghdr *msg, size_t len) | |||
839 | if (!(opt->opt_nflen|opt->opt_flen)) | 840 | if (!(opt->opt_nflen|opt->opt_flen)) |
840 | opt = NULL; | 841 | opt = NULL; |
841 | } | 842 | } |
842 | if (!opt) | 843 | if (!opt) { |
843 | opt = np->opt; | 844 | opt = txopt_get(np); |
845 | opt_to_free = opt; | ||
846 | } | ||
844 | if (flowlabel) | 847 | if (flowlabel) |
845 | opt = fl6_merge_options(&opt_space, flowlabel, opt); | 848 | opt = fl6_merge_options(&opt_space, flowlabel, opt); |
846 | opt = ipv6_fixup_options(&opt_space, opt); | 849 | opt = ipv6_fixup_options(&opt_space, opt); |
@@ -906,6 +909,7 @@ done: | |||
906 | dst_release(dst); | 909 | dst_release(dst); |
907 | out: | 910 | out: |
908 | fl6_sock_release(flowlabel); | 911 | fl6_sock_release(flowlabel); |
912 | txopt_put(opt_to_free); | ||
909 | return err < 0 ? err : len; | 913 | return err < 0 ? err : len; |
910 | do_confirm: | 914 | do_confirm: |
911 | dst_confirm(dst); | 915 | dst_confirm(dst); |
diff --git a/net/ipv6/syncookies.c b/net/ipv6/syncookies.c index bb8f2fa1c7fb..eaf7ac496d50 100644 --- a/net/ipv6/syncookies.c +++ b/net/ipv6/syncookies.c | |||
@@ -222,7 +222,7 @@ struct sock *cookie_v6_check(struct sock *sk, struct sk_buff *skb) | |||
222 | memset(&fl6, 0, sizeof(fl6)); | 222 | memset(&fl6, 0, sizeof(fl6)); |
223 | fl6.flowi6_proto = IPPROTO_TCP; | 223 | fl6.flowi6_proto = IPPROTO_TCP; |
224 | fl6.daddr = ireq->ir_v6_rmt_addr; | 224 | fl6.daddr = ireq->ir_v6_rmt_addr; |
225 | final_p = fl6_update_dst(&fl6, np->opt, &final); | 225 | final_p = fl6_update_dst(&fl6, rcu_dereference(np->opt), &final); |
226 | fl6.saddr = ireq->ir_v6_loc_addr; | 226 | fl6.saddr = ireq->ir_v6_loc_addr; |
227 | fl6.flowi6_oif = sk->sk_bound_dev_if; | 227 | fl6.flowi6_oif = sk->sk_bound_dev_if; |
228 | fl6.flowi6_mark = ireq->ir_mark; | 228 | fl6.flowi6_mark = ireq->ir_mark; |
diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c index c5429a636f1a..6a50bb4a0dae 100644 --- a/net/ipv6/tcp_ipv6.c +++ b/net/ipv6/tcp_ipv6.c | |||
@@ -120,6 +120,7 @@ static int tcp_v6_connect(struct sock *sk, struct sockaddr *uaddr, | |||
120 | struct ipv6_pinfo *np = inet6_sk(sk); | 120 | struct ipv6_pinfo *np = inet6_sk(sk); |
121 | struct tcp_sock *tp = tcp_sk(sk); | 121 | struct tcp_sock *tp = tcp_sk(sk); |
122 | struct in6_addr *saddr = NULL, *final_p, final; | 122 | struct in6_addr *saddr = NULL, *final_p, final; |
123 | struct ipv6_txoptions *opt; | ||
123 | struct flowi6 fl6; | 124 | struct flowi6 fl6; |
124 | struct dst_entry *dst; | 125 | struct dst_entry *dst; |
125 | int addr_type; | 126 | int addr_type; |
@@ -235,7 +236,8 @@ static int tcp_v6_connect(struct sock *sk, struct sockaddr *uaddr, | |||
235 | fl6.fl6_dport = usin->sin6_port; | 236 | fl6.fl6_dport = usin->sin6_port; |
236 | fl6.fl6_sport = inet->inet_sport; | 237 | fl6.fl6_sport = inet->inet_sport; |
237 | 238 | ||
238 | final_p = fl6_update_dst(&fl6, np->opt, &final); | 239 | opt = rcu_dereference_protected(np->opt, sock_owned_by_user(sk)); |
240 | final_p = fl6_update_dst(&fl6, opt, &final); | ||
239 | 241 | ||
240 | security_sk_classify_flow(sk, flowi6_to_flowi(&fl6)); | 242 | security_sk_classify_flow(sk, flowi6_to_flowi(&fl6)); |
241 | 243 | ||
@@ -263,9 +265,9 @@ static int tcp_v6_connect(struct sock *sk, struct sockaddr *uaddr, | |||
263 | tcp_fetch_timewait_stamp(sk, dst); | 265 | tcp_fetch_timewait_stamp(sk, dst); |
264 | 266 | ||
265 | icsk->icsk_ext_hdr_len = 0; | 267 | icsk->icsk_ext_hdr_len = 0; |
266 | if (np->opt) | 268 | if (opt) |
267 | icsk->icsk_ext_hdr_len = (np->opt->opt_flen + | 269 | icsk->icsk_ext_hdr_len = opt->opt_flen + |
268 | np->opt->opt_nflen); | 270 | opt->opt_nflen; |
269 | 271 | ||
270 | tp->rx_opt.mss_clamp = IPV6_MIN_MTU - sizeof(struct tcphdr) - sizeof(struct ipv6hdr); | 272 | tp->rx_opt.mss_clamp = IPV6_MIN_MTU - sizeof(struct tcphdr) - sizeof(struct ipv6hdr); |
271 | 273 | ||
@@ -461,7 +463,8 @@ static int tcp_v6_send_synack(const struct sock *sk, struct dst_entry *dst, | |||
461 | if (np->repflow && ireq->pktopts) | 463 | if (np->repflow && ireq->pktopts) |
462 | fl6->flowlabel = ip6_flowlabel(ipv6_hdr(ireq->pktopts)); | 464 | fl6->flowlabel = ip6_flowlabel(ipv6_hdr(ireq->pktopts)); |
463 | 465 | ||
464 | err = ip6_xmit(sk, skb, fl6, np->opt, np->tclass); | 466 | err = ip6_xmit(sk, skb, fl6, rcu_dereference(np->opt), |
467 | np->tclass); | ||
465 | err = net_xmit_eval(err); | 468 | err = net_xmit_eval(err); |
466 | } | 469 | } |
467 | 470 | ||
@@ -972,6 +975,7 @@ static struct sock *tcp_v6_syn_recv_sock(const struct sock *sk, struct sk_buff * | |||
972 | struct inet_request_sock *ireq; | 975 | struct inet_request_sock *ireq; |
973 | struct ipv6_pinfo *newnp; | 976 | struct ipv6_pinfo *newnp; |
974 | const struct ipv6_pinfo *np = inet6_sk(sk); | 977 | const struct ipv6_pinfo *np = inet6_sk(sk); |
978 | struct ipv6_txoptions *opt; | ||
975 | struct tcp6_sock *newtcp6sk; | 979 | struct tcp6_sock *newtcp6sk; |
976 | struct inet_sock *newinet; | 980 | struct inet_sock *newinet; |
977 | struct tcp_sock *newtp; | 981 | struct tcp_sock *newtp; |
@@ -1098,13 +1102,15 @@ static struct sock *tcp_v6_syn_recv_sock(const struct sock *sk, struct sk_buff * | |||
1098 | but we make one more one thing there: reattach optmem | 1102 | but we make one more one thing there: reattach optmem |
1099 | to newsk. | 1103 | to newsk. |
1100 | */ | 1104 | */ |
1101 | if (np->opt) | 1105 | opt = rcu_dereference(np->opt); |
1102 | newnp->opt = ipv6_dup_options(newsk, np->opt); | 1106 | if (opt) { |
1103 | 1107 | opt = ipv6_dup_options(newsk, opt); | |
1108 | RCU_INIT_POINTER(newnp->opt, opt); | ||
1109 | } | ||
1104 | inet_csk(newsk)->icsk_ext_hdr_len = 0; | 1110 | inet_csk(newsk)->icsk_ext_hdr_len = 0; |
1105 | if (newnp->opt) | 1111 | if (opt) |
1106 | inet_csk(newsk)->icsk_ext_hdr_len = (newnp->opt->opt_nflen + | 1112 | inet_csk(newsk)->icsk_ext_hdr_len = opt->opt_nflen + |
1107 | newnp->opt->opt_flen); | 1113 | opt->opt_flen; |
1108 | 1114 | ||
1109 | tcp_ca_openreq_child(newsk, dst); | 1115 | tcp_ca_openreq_child(newsk, dst); |
1110 | 1116 | ||
diff --git a/net/ipv6/udp.c b/net/ipv6/udp.c index 01bcb49619ee..9da3287a3923 100644 --- a/net/ipv6/udp.c +++ b/net/ipv6/udp.c | |||
@@ -1110,6 +1110,7 @@ int udpv6_sendmsg(struct sock *sk, struct msghdr *msg, size_t len) | |||
1110 | DECLARE_SOCKADDR(struct sockaddr_in6 *, sin6, msg->msg_name); | 1110 | DECLARE_SOCKADDR(struct sockaddr_in6 *, sin6, msg->msg_name); |
1111 | struct in6_addr *daddr, *final_p, final; | 1111 | struct in6_addr *daddr, *final_p, final; |
1112 | struct ipv6_txoptions *opt = NULL; | 1112 | struct ipv6_txoptions *opt = NULL; |
1113 | struct ipv6_txoptions *opt_to_free = NULL; | ||
1113 | struct ip6_flowlabel *flowlabel = NULL; | 1114 | struct ip6_flowlabel *flowlabel = NULL; |
1114 | struct flowi6 fl6; | 1115 | struct flowi6 fl6; |
1115 | struct dst_entry *dst; | 1116 | struct dst_entry *dst; |
@@ -1263,8 +1264,10 @@ do_udp_sendmsg: | |||
1263 | opt = NULL; | 1264 | opt = NULL; |
1264 | connected = 0; | 1265 | connected = 0; |
1265 | } | 1266 | } |
1266 | if (!opt) | 1267 | if (!opt) { |
1267 | opt = np->opt; | 1268 | opt = txopt_get(np); |
1269 | opt_to_free = opt; | ||
1270 | } | ||
1268 | if (flowlabel) | 1271 | if (flowlabel) |
1269 | opt = fl6_merge_options(&opt_space, flowlabel, opt); | 1272 | opt = fl6_merge_options(&opt_space, flowlabel, opt); |
1270 | opt = ipv6_fixup_options(&opt_space, opt); | 1273 | opt = ipv6_fixup_options(&opt_space, opt); |
@@ -1373,6 +1376,7 @@ release_dst: | |||
1373 | out: | 1376 | out: |
1374 | dst_release(dst); | 1377 | dst_release(dst); |
1375 | fl6_sock_release(flowlabel); | 1378 | fl6_sock_release(flowlabel); |
1379 | txopt_put(opt_to_free); | ||
1376 | if (!err) | 1380 | if (!err) |
1377 | return len; | 1381 | return len; |
1378 | /* | 1382 | /* |
diff --git a/net/l2tp/l2tp_ip6.c b/net/l2tp/l2tp_ip6.c index aca38d8aed8e..a2c8747d2936 100644 --- a/net/l2tp/l2tp_ip6.c +++ b/net/l2tp/l2tp_ip6.c | |||
@@ -486,6 +486,7 @@ static int l2tp_ip6_sendmsg(struct sock *sk, struct msghdr *msg, size_t len) | |||
486 | DECLARE_SOCKADDR(struct sockaddr_l2tpip6 *, lsa, msg->msg_name); | 486 | DECLARE_SOCKADDR(struct sockaddr_l2tpip6 *, lsa, msg->msg_name); |
487 | struct in6_addr *daddr, *final_p, final; | 487 | struct in6_addr *daddr, *final_p, final; |
488 | struct ipv6_pinfo *np = inet6_sk(sk); | 488 | struct ipv6_pinfo *np = inet6_sk(sk); |
489 | struct ipv6_txoptions *opt_to_free = NULL; | ||
489 | struct ipv6_txoptions *opt = NULL; | 490 | struct ipv6_txoptions *opt = NULL; |
490 | struct ip6_flowlabel *flowlabel = NULL; | 491 | struct ip6_flowlabel *flowlabel = NULL; |
491 | struct dst_entry *dst = NULL; | 492 | struct dst_entry *dst = NULL; |
@@ -575,8 +576,10 @@ static int l2tp_ip6_sendmsg(struct sock *sk, struct msghdr *msg, size_t len) | |||
575 | opt = NULL; | 576 | opt = NULL; |
576 | } | 577 | } |
577 | 578 | ||
578 | if (opt == NULL) | 579 | if (!opt) { |
579 | opt = np->opt; | 580 | opt = txopt_get(np); |
581 | opt_to_free = opt; | ||
582 | } | ||
580 | if (flowlabel) | 583 | if (flowlabel) |
581 | opt = fl6_merge_options(&opt_space, flowlabel, opt); | 584 | opt = fl6_merge_options(&opt_space, flowlabel, opt); |
582 | opt = ipv6_fixup_options(&opt_space, opt); | 585 | opt = ipv6_fixup_options(&opt_space, opt); |
@@ -631,6 +634,7 @@ done: | |||
631 | dst_release(dst); | 634 | dst_release(dst); |
632 | out: | 635 | out: |
633 | fl6_sock_release(flowlabel); | 636 | fl6_sock_release(flowlabel); |
637 | txopt_put(opt_to_free); | ||
634 | 638 | ||
635 | return err < 0 ? err : len; | 639 | return err < 0 ? err : len; |
636 | 640 | ||