ipv6: add complete rcu protection around np->opt
[ Upstream commit 45f6fad84cc305103b28d73482b344d7f5b76f39 ] 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 b/28746669 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> Signed-off-by: Sasha Levin <sasha.levin@oracle.com> Signed-off-by: Dennis Cagle <dcagle@codeaurora.org> Git-commit: 45f6fad84cc305103b28d73482b344d7f5b76f39 Git-repo: https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git (cherry picked from commit 46ddb98e2018a5a62cefa75b3c80882850c91e39) Change-Id: Ie893308ab0950d8d5494c090e04b8971940a2549
This commit is contained in:
parent
dcb32ab988
commit
14d783ad33
|
@ -222,7 +222,7 @@ struct ipv6_pinfo {
|
|||
struct ipv6_ac_socklist *ipv6_ac_list;
|
||||
struct ipv6_fl_socklist __rcu *ipv6_fl_list;
|
||||
|
||||
struct ipv6_txoptions *opt;
|
||||
struct ipv6_txoptions __rcu *opt;
|
||||
struct sk_buff *pktoptions;
|
||||
struct sk_buff *rxpmtu;
|
||||
struct {
|
||||
|
|
|
@ -203,6 +203,7 @@ extern rwlock_t ip6_ra_lock;
|
|||
*/
|
||||
|
||||
struct ipv6_txoptions {
|
||||
atomic_t refcnt;
|
||||
/* Length of this structure */
|
||||
int tot_len;
|
||||
|
||||
|
@ -215,7 +216,7 @@ struct ipv6_txoptions {
|
|||
struct ipv6_opt_hdr *dst0opt;
|
||||
struct ipv6_rt_hdr *srcrt; /* Routing Header */
|
||||
struct ipv6_opt_hdr *dst1opt;
|
||||
|
||||
struct rcu_head rcu;
|
||||
/* Option buffer, as read by IPV6_PKTOPTIONS, starts here. */
|
||||
};
|
||||
|
||||
|
@ -254,6 +255,23 @@ extern void fl6_free_socklist(struct sock *sk);
|
|||
extern int ipv6_flowlabel_opt(struct sock *sk, char __user *optval, int optlen);
|
||||
extern int ip6_flowlabel_init(void);
|
||||
extern void ip6_flowlabel_cleanup(void);
|
||||
static inline struct ipv6_txoptions *txopt_get(const struct ipv6_pinfo *np)
|
||||
{
|
||||
struct ipv6_txoptions *opt;
|
||||
|
||||
rcu_read_lock();
|
||||
opt = rcu_dereference(np->opt);
|
||||
if (opt && !atomic_inc_not_zero(&opt->refcnt))
|
||||
opt = NULL;
|
||||
rcu_read_unlock();
|
||||
return opt;
|
||||
}
|
||||
|
||||
static inline void txopt_put(struct ipv6_txoptions *opt)
|
||||
{
|
||||
if (opt && atomic_dec_and_test(&opt->refcnt))
|
||||
kfree_rcu(opt, rcu);
|
||||
}
|
||||
|
||||
static inline void fl6_sock_release(struct ip6_flowlabel *fl)
|
||||
{
|
||||
|
|
|
@ -235,7 +235,9 @@ static int dccp_v6_send_response(struct sock *sk, struct request_sock *req)
|
|||
security_req_classify_flow(req, flowi6_to_flowi(&fl6));
|
||||
|
||||
|
||||
final_p = fl6_update_dst(&fl6, np->opt, &final);
|
||||
rcu_read_lock();
|
||||
final_p = fl6_update_dst(&fl6, rcu_dereference(np->opt), &final);
|
||||
rcu_read_unlock();
|
||||
|
||||
dst = ip6_dst_lookup_flow(sk, &fl6, final_p, false);
|
||||
if (IS_ERR(dst)) {
|
||||
|
@ -252,7 +254,10 @@ static int dccp_v6_send_response(struct sock *sk, struct request_sock *req)
|
|||
&ireq6->loc_addr,
|
||||
&ireq6->rmt_addr);
|
||||
fl6.daddr = ireq6->rmt_addr;
|
||||
err = ip6_xmit(sk, skb, &fl6, np->opt, np->tclass);
|
||||
rcu_read_lock();
|
||||
err = ip6_xmit(sk, skb, &fl6, rcu_dereference(np->opt),
|
||||
np->tclass);
|
||||
rcu_read_unlock();
|
||||
err = net_xmit_eval(err);
|
||||
}
|
||||
|
||||
|
@ -448,6 +453,7 @@ static struct sock *dccp_v6_request_recv_sock(struct sock *sk,
|
|||
{
|
||||
struct inet6_request_sock *ireq6 = inet6_rsk(req);
|
||||
struct ipv6_pinfo *newnp, *np = inet6_sk(sk);
|
||||
struct ipv6_txoptions *opt;
|
||||
struct inet_sock *newinet;
|
||||
struct dccp6_sock *newdp6;
|
||||
struct sock *newsk;
|
||||
|
@ -571,13 +577,15 @@ static struct sock *dccp_v6_request_recv_sock(struct sock *sk,
|
|||
* Yes, keeping reference count would be much more clever, but we make
|
||||
* one more one thing there: reattach optmem to newsk.
|
||||
*/
|
||||
if (np->opt != NULL)
|
||||
newnp->opt = ipv6_dup_options(newsk, np->opt);
|
||||
|
||||
opt = rcu_dereference(np->opt);
|
||||
if (opt) {
|
||||
opt = ipv6_dup_options(newsk, opt);
|
||||
RCU_INIT_POINTER(newnp->opt, opt);
|
||||
}
|
||||
inet_csk(newsk)->icsk_ext_hdr_len = 0;
|
||||
if (newnp->opt != NULL)
|
||||
inet_csk(newsk)->icsk_ext_hdr_len = (newnp->opt->opt_nflen +
|
||||
newnp->opt->opt_flen);
|
||||
if (opt)
|
||||
inet_csk(newsk)->icsk_ext_hdr_len = opt->opt_nflen +
|
||||
opt->opt_flen;
|
||||
|
||||
dccp_sync_mss(newsk, dst_mtu(dst));
|
||||
|
||||
|
@ -829,6 +837,7 @@ static int dccp_v6_connect(struct sock *sk, struct sockaddr *uaddr,
|
|||
struct ipv6_pinfo *np = inet6_sk(sk);
|
||||
struct dccp_sock *dp = dccp_sk(sk);
|
||||
struct in6_addr *saddr = NULL, *final_p, final;
|
||||
struct ipv6_txoptions *opt;
|
||||
struct flowi6 fl6;
|
||||
struct dst_entry *dst;
|
||||
int addr_type;
|
||||
|
@ -931,7 +940,8 @@ static int dccp_v6_connect(struct sock *sk, struct sockaddr *uaddr,
|
|||
fl6.fl6_sport = inet->inet_sport;
|
||||
security_sk_classify_flow(sk, flowi6_to_flowi(&fl6));
|
||||
|
||||
final_p = fl6_update_dst(&fl6, np->opt, &final);
|
||||
opt = rcu_dereference_protected(np->opt, sock_owned_by_user(sk));
|
||||
final_p = fl6_update_dst(&fl6, opt, &final);
|
||||
|
||||
dst = ip6_dst_lookup_flow(sk, &fl6, final_p, true);
|
||||
if (IS_ERR(dst)) {
|
||||
|
@ -951,9 +961,8 @@ static int dccp_v6_connect(struct sock *sk, struct sockaddr *uaddr,
|
|||
__ip6_dst_store(sk, dst, NULL, NULL);
|
||||
|
||||
icsk->icsk_ext_hdr_len = 0;
|
||||
if (np->opt != NULL)
|
||||
icsk->icsk_ext_hdr_len = (np->opt->opt_flen +
|
||||
np->opt->opt_nflen);
|
||||
if (opt)
|
||||
icsk->icsk_ext_hdr_len = opt->opt_flen + opt->opt_nflen;
|
||||
|
||||
inet->inet_dport = usin->sin6_port;
|
||||
|
||||
|
|
|
@ -448,9 +448,11 @@ void inet6_destroy_sock(struct sock *sk)
|
|||
|
||||
/* Free tx options */
|
||||
|
||||
opt = xchg(&np->opt, NULL);
|
||||
if (opt != NULL)
|
||||
sock_kfree_s(sk, opt, opt->tot_len);
|
||||
opt = xchg((__force struct ipv6_txoptions **)&np->opt, NULL);
|
||||
if (opt) {
|
||||
atomic_sub(opt->tot_len, &sk->sk_omem_alloc);
|
||||
txopt_put(opt);
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(inet6_destroy_sock);
|
||||
|
||||
|
@ -697,7 +699,10 @@ int inet6_sk_rebuild_header(struct sock *sk)
|
|||
fl6.flowi6_uid = sock_i_uid(sk);
|
||||
security_sk_classify_flow(sk, flowi6_to_flowi(&fl6));
|
||||
|
||||
final_p = fl6_update_dst(&fl6, np->opt, &final);
|
||||
rcu_read_lock();
|
||||
final_p = fl6_update_dst(&fl6, rcu_dereference(np->opt),
|
||||
&final);
|
||||
rcu_read_unlock();
|
||||
|
||||
dst = ip6_dst_lookup_flow(sk, &fl6, final_p, false);
|
||||
if (IS_ERR(dst)) {
|
||||
|
|
|
@ -169,8 +169,10 @@ ipv4_connected:
|
|||
|
||||
security_sk_classify_flow(sk, flowi6_to_flowi(&fl6));
|
||||
|
||||
opt = flowlabel ? flowlabel->opt : np->opt;
|
||||
rcu_read_lock();
|
||||
opt = flowlabel ? flowlabel->opt : rcu_dereference(np->opt);
|
||||
final_p = fl6_update_dst(&fl6, opt, &final);
|
||||
rcu_read_unlock();
|
||||
|
||||
dst = ip6_dst_lookup_flow(sk, &fl6, final_p, true);
|
||||
err = 0;
|
||||
|
|
|
@ -727,6 +727,7 @@ ipv6_dup_options(struct sock *sk, struct ipv6_txoptions *opt)
|
|||
*((char **)&opt2->dst1opt) += dif;
|
||||
if (opt2->srcrt)
|
||||
*((char **)&opt2->srcrt) += dif;
|
||||
atomic_set(&opt2->refcnt, 1);
|
||||
}
|
||||
return opt2;
|
||||
}
|
||||
|
@ -790,7 +791,7 @@ ipv6_renew_options(struct sock *sk, struct ipv6_txoptions *opt,
|
|||
return ERR_PTR(-ENOBUFS);
|
||||
|
||||
memset(opt2, 0, tot_len);
|
||||
|
||||
atomic_set(&opt2->refcnt, 1);
|
||||
opt2->tot_len = tot_len;
|
||||
p = (char *)(opt2 + 1);
|
||||
|
||||
|
|
|
@ -78,7 +78,9 @@ struct dst_entry *inet6_csk_route_req(struct sock *sk,
|
|||
memset(fl6, 0, sizeof(*fl6));
|
||||
fl6->flowi6_proto = IPPROTO_TCP;
|
||||
fl6->daddr = treq->rmt_addr;
|
||||
final_p = fl6_update_dst(fl6, np->opt, &final);
|
||||
rcu_read_lock();
|
||||
final_p = fl6_update_dst(fl6, rcu_dereference(np->opt), &final);
|
||||
rcu_read_unlock();
|
||||
fl6->saddr = treq->loc_addr;
|
||||
fl6->flowi6_oif = treq->iif;
|
||||
fl6->flowi6_mark = inet_rsk(req)->ir_mark;
|
||||
|
@ -214,7 +216,9 @@ static struct dst_entry *inet6_csk_route_socket(struct sock *sk,
|
|||
fl6->fl6_dport = inet->inet_dport;
|
||||
security_sk_classify_flow(sk, flowi6_to_flowi(fl6));
|
||||
|
||||
final_p = fl6_update_dst(fl6, np->opt, &final);
|
||||
rcu_read_lock();
|
||||
final_p = fl6_update_dst(fl6, rcu_dereference(np->opt), &final);
|
||||
rcu_read_unlock();
|
||||
|
||||
dst = __inet6_csk_dst_check(sk, np->dst_cookie);
|
||||
if (!dst) {
|
||||
|
@ -248,7 +252,8 @@ int inet6_csk_xmit(struct sk_buff *skb, struct flowi *fl_unused)
|
|||
/* Restore final destination back after routing done */
|
||||
fl6.daddr = np->daddr;
|
||||
|
||||
res = ip6_xmit(sk, skb, &fl6, np->opt, np->tclass);
|
||||
res = ip6_xmit(sk, skb, &fl6, rcu_dereference(np->opt),
|
||||
np->tclass);
|
||||
rcu_read_unlock();
|
||||
return res;
|
||||
}
|
||||
|
|
|
@ -110,10 +110,12 @@ struct ipv6_txoptions *ipv6_update_options(struct sock *sk,
|
|||
icsk->icsk_ext_hdr_len = opt->opt_flen + opt->opt_nflen;
|
||||
icsk->icsk_sync_mss(sk, icsk->icsk_pmtu_cookie);
|
||||
}
|
||||
opt = xchg(&inet6_sk(sk)->opt, opt);
|
||||
opt = xchg((__force struct ipv6_txoptions **)&inet6_sk(sk)->opt,
|
||||
opt);
|
||||
} else {
|
||||
spin_lock(&sk->sk_dst_lock);
|
||||
opt = xchg(&inet6_sk(sk)->opt, opt);
|
||||
opt = xchg((__force struct ipv6_txoptions **)&inet6_sk(sk)->opt,
|
||||
opt);
|
||||
spin_unlock(&sk->sk_dst_lock);
|
||||
}
|
||||
sk_dst_reset(sk);
|
||||
|
@ -213,9 +215,12 @@ static int do_ipv6_setsockopt(struct sock *sk, int level, int optname,
|
|||
sk->sk_socket->ops = &inet_dgram_ops;
|
||||
sk->sk_family = PF_INET;
|
||||
}
|
||||
opt = xchg(&np->opt, NULL);
|
||||
if (opt)
|
||||
sock_kfree_s(sk, opt, opt->tot_len);
|
||||
opt = xchg((__force struct ipv6_txoptions **)&np->opt,
|
||||
NULL);
|
||||
if (opt) {
|
||||
atomic_sub(opt->tot_len, &sk->sk_omem_alloc);
|
||||
txopt_put(opt);
|
||||
}
|
||||
pktopt = xchg(&np->pktoptions, NULL);
|
||||
kfree_skb(pktopt);
|
||||
|
||||
|
@ -385,7 +390,8 @@ static int do_ipv6_setsockopt(struct sock *sk, int level, int optname,
|
|||
if (optname != IPV6_RTHDR && !ns_capable(net->user_ns, CAP_NET_RAW))
|
||||
break;
|
||||
|
||||
opt = ipv6_renew_options(sk, np->opt, optname,
|
||||
opt = rcu_dereference_protected(np->opt, sock_owned_by_user(sk));
|
||||
opt = ipv6_renew_options(sk, opt, optname,
|
||||
(struct ipv6_opt_hdr __user *)optval,
|
||||
optlen);
|
||||
if (IS_ERR(opt)) {
|
||||
|
@ -414,8 +420,10 @@ static int do_ipv6_setsockopt(struct sock *sk, int level, int optname,
|
|||
retv = 0;
|
||||
opt = ipv6_update_options(sk, opt);
|
||||
sticky_done:
|
||||
if (opt)
|
||||
sock_kfree_s(sk, opt, opt->tot_len);
|
||||
if (opt) {
|
||||
atomic_sub(opt->tot_len, &sk->sk_omem_alloc);
|
||||
txopt_put(opt);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -468,6 +476,7 @@ sticky_done:
|
|||
break;
|
||||
|
||||
memset(opt, 0, sizeof(*opt));
|
||||
atomic_set(&opt->refcnt, 1);
|
||||
opt->tot_len = sizeof(*opt) + optlen;
|
||||
retv = -EFAULT;
|
||||
if (copy_from_user(opt+1, optval, optlen))
|
||||
|
@ -484,8 +493,10 @@ update:
|
|||
retv = 0;
|
||||
opt = ipv6_update_options(sk, opt);
|
||||
done:
|
||||
if (opt)
|
||||
sock_kfree_s(sk, opt, opt->tot_len);
|
||||
if (opt) {
|
||||
atomic_sub(opt->tot_len, &sk->sk_omem_alloc);
|
||||
txopt_put(opt);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case IPV6_UNICAST_HOPS:
|
||||
|
@ -1085,10 +1096,11 @@ static int do_ipv6_getsockopt(struct sock *sk, int level, int optname,
|
|||
case IPV6_RTHDR:
|
||||
case IPV6_DSTOPTS:
|
||||
{
|
||||
struct ipv6_txoptions *opt;
|
||||
|
||||
lock_sock(sk);
|
||||
len = ipv6_getsockopt_sticky(sk, np->opt,
|
||||
optname, optval, len);
|
||||
opt = rcu_dereference_protected(np->opt, sock_owned_by_user(sk));
|
||||
len = ipv6_getsockopt_sticky(sk, opt, optname, optval, len);
|
||||
release_sock(sk);
|
||||
/* check if ipv6_getsockopt_sticky() returns err code */
|
||||
if (len < 0)
|
||||
|
|
|
@ -726,6 +726,7 @@ static int rawv6_probe_proto_opt(struct flowi6 *fl6, struct msghdr *msg)
|
|||
static int rawv6_sendmsg(struct kiocb *iocb, struct sock *sk,
|
||||
struct msghdr *msg, size_t len)
|
||||
{
|
||||
struct ipv6_txoptions *opt_to_free = NULL;
|
||||
struct ipv6_txoptions opt_space;
|
||||
struct sockaddr_in6 * sin6 = (struct sockaddr_in6 *) msg->msg_name;
|
||||
struct in6_addr *daddr, *final_p, final;
|
||||
|
@ -833,8 +834,10 @@ static int rawv6_sendmsg(struct kiocb *iocb, struct sock *sk,
|
|||
if (!(opt->opt_nflen|opt->opt_flen))
|
||||
opt = NULL;
|
||||
}
|
||||
if (opt == NULL)
|
||||
opt = np->opt;
|
||||
if (!opt) {
|
||||
opt = txopt_get(np);
|
||||
opt_to_free = opt;
|
||||
}
|
||||
if (flowlabel)
|
||||
opt = fl6_merge_options(&opt_space, flowlabel, opt);
|
||||
opt = ipv6_fixup_options(&opt_space, opt);
|
||||
|
@ -901,7 +904,8 @@ done:
|
|||
dst_release(dst);
|
||||
out:
|
||||
fl6_sock_release(flowlabel);
|
||||
return err<0?err:len;
|
||||
txopt_put(opt_to_free);
|
||||
return err < 0 ? err : len;
|
||||
do_confirm:
|
||||
dst_confirm(dst);
|
||||
if (!(msg->msg_flags & MSG_PROBE) || len)
|
||||
|
|
|
@ -237,7 +237,7 @@ struct sock *cookie_v6_check(struct sock *sk, struct sk_buff *skb)
|
|||
memset(&fl6, 0, sizeof(fl6));
|
||||
fl6.flowi6_proto = IPPROTO_TCP;
|
||||
fl6.daddr = ireq6->rmt_addr;
|
||||
final_p = fl6_update_dst(&fl6, np->opt, &final);
|
||||
final_p = fl6_update_dst(&fl6, rcu_dereference(np->opt), &final);
|
||||
fl6.saddr = ireq6->loc_addr;
|
||||
fl6.flowi6_oif = sk->sk_bound_dev_if;
|
||||
fl6.flowi6_mark = ireq->ir_mark;
|
||||
|
|
|
@ -133,6 +133,7 @@ static int tcp_v6_connect(struct sock *sk, struct sockaddr *uaddr,
|
|||
struct ipv6_pinfo *np = inet6_sk(sk);
|
||||
struct tcp_sock *tp = tcp_sk(sk);
|
||||
struct in6_addr *saddr = NULL, *final_p, final;
|
||||
struct ipv6_txoptions *opt;
|
||||
struct rt6_info *rt;
|
||||
struct flowi6 fl6;
|
||||
struct dst_entry *dst;
|
||||
|
@ -254,7 +255,8 @@ static int tcp_v6_connect(struct sock *sk, struct sockaddr *uaddr,
|
|||
fl6.fl6_sport = inet->inet_sport;
|
||||
fl6.flowi6_uid = sock_i_uid(sk);
|
||||
|
||||
final_p = fl6_update_dst(&fl6, np->opt, &final);
|
||||
opt = rcu_dereference_protected(np->opt, sock_owned_by_user(sk));
|
||||
final_p = fl6_update_dst(&fl6, opt, &final);
|
||||
|
||||
security_sk_classify_flow(sk, flowi6_to_flowi(&fl6));
|
||||
|
||||
|
@ -283,9 +285,9 @@ static int tcp_v6_connect(struct sock *sk, struct sockaddr *uaddr,
|
|||
tcp_fetch_timewait_stamp(sk, dst);
|
||||
|
||||
icsk->icsk_ext_hdr_len = 0;
|
||||
if (np->opt)
|
||||
icsk->icsk_ext_hdr_len = (np->opt->opt_flen +
|
||||
np->opt->opt_nflen);
|
||||
if (opt)
|
||||
icsk->icsk_ext_hdr_len = opt->opt_flen +
|
||||
opt->opt_nflen;
|
||||
|
||||
tp->rx_opt.mss_clamp = IPV6_MIN_MTU - sizeof(struct tcphdr) - sizeof(struct ipv6hdr);
|
||||
|
||||
|
@ -481,7 +483,8 @@ static int tcp_v6_send_synack(struct sock *sk, struct dst_entry *dst,
|
|||
|
||||
fl6->daddr = treq->rmt_addr;
|
||||
skb_set_queue_mapping(skb, queue_mapping);
|
||||
err = ip6_xmit(sk, skb, fl6, np->opt, np->tclass);
|
||||
err = ip6_xmit(sk, skb, fl6, rcu_dereference(np->opt),
|
||||
np->tclass);
|
||||
err = net_xmit_eval(err);
|
||||
}
|
||||
|
||||
|
@ -1090,6 +1093,7 @@ static struct sock * tcp_v6_syn_recv_sock(struct sock *sk, struct sk_buff *skb,
|
|||
struct inet6_request_sock *treq;
|
||||
struct ipv6_pinfo *newnp, *np = inet6_sk(sk);
|
||||
struct tcp6_sock *newtcp6sk;
|
||||
struct ipv6_txoptions *opt;
|
||||
struct inet_sock *newinet;
|
||||
struct tcp_sock *newtp;
|
||||
struct sock *newsk;
|
||||
|
@ -1223,13 +1227,15 @@ static struct sock * tcp_v6_syn_recv_sock(struct sock *sk, struct sk_buff *skb,
|
|||
but we make one more one thing there: reattach optmem
|
||||
to newsk.
|
||||
*/
|
||||
if (np->opt)
|
||||
newnp->opt = ipv6_dup_options(newsk, np->opt);
|
||||
|
||||
opt = rcu_dereference(np->opt);
|
||||
if (opt) {
|
||||
opt = ipv6_dup_options(newsk, opt);
|
||||
RCU_INIT_POINTER(newnp->opt, opt);
|
||||
}
|
||||
inet_csk(newsk)->icsk_ext_hdr_len = 0;
|
||||
if (newnp->opt)
|
||||
inet_csk(newsk)->icsk_ext_hdr_len = (newnp->opt->opt_nflen +
|
||||
newnp->opt->opt_flen);
|
||||
if (opt)
|
||||
inet_csk(newsk)->icsk_ext_hdr_len = opt->opt_nflen +
|
||||
opt->opt_flen;
|
||||
|
||||
tcp_mtup_init(newsk);
|
||||
tcp_sync_mss(newsk, dst_mtu(dst));
|
||||
|
|
|
@ -1015,6 +1015,7 @@ int udpv6_sendmsg(struct kiocb *iocb, struct sock *sk,
|
|||
struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *) msg->msg_name;
|
||||
struct in6_addr *daddr, *final_p, final;
|
||||
struct ipv6_txoptions *opt = NULL;
|
||||
struct ipv6_txoptions *opt_to_free = NULL;
|
||||
struct ip6_flowlabel *flowlabel = NULL;
|
||||
struct flowi6 fl6;
|
||||
struct dst_entry *dst;
|
||||
|
@ -1169,8 +1170,10 @@ do_udp_sendmsg:
|
|||
opt = NULL;
|
||||
connected = 0;
|
||||
}
|
||||
if (opt == NULL)
|
||||
opt = np->opt;
|
||||
if (!opt) {
|
||||
opt = txopt_get(np);
|
||||
opt_to_free = opt;
|
||||
}
|
||||
if (flowlabel)
|
||||
opt = fl6_merge_options(&opt_space, flowlabel, opt);
|
||||
opt = ipv6_fixup_options(&opt_space, opt);
|
||||
|
@ -1271,6 +1274,7 @@ do_append_data:
|
|||
out:
|
||||
dst_release(dst);
|
||||
fl6_sock_release(flowlabel);
|
||||
txopt_put(opt_to_free);
|
||||
if (!err)
|
||||
return len;
|
||||
/*
|
||||
|
|
|
@ -485,6 +485,7 @@ static int l2tp_ip6_sendmsg(struct kiocb *iocb, struct sock *sk,
|
|||
(struct sockaddr_l2tpip6 *) msg->msg_name;
|
||||
struct in6_addr *daddr, *final_p, final;
|
||||
struct ipv6_pinfo *np = inet6_sk(sk);
|
||||
struct ipv6_txoptions *opt_to_free = NULL;
|
||||
struct ipv6_txoptions *opt = NULL;
|
||||
struct ip6_flowlabel *flowlabel = NULL;
|
||||
struct dst_entry *dst = NULL;
|
||||
|
@ -575,8 +576,10 @@ static int l2tp_ip6_sendmsg(struct kiocb *iocb, struct sock *sk,
|
|||
opt = NULL;
|
||||
}
|
||||
|
||||
if (opt == NULL)
|
||||
opt = np->opt;
|
||||
if (!opt) {
|
||||
opt = txopt_get(np);
|
||||
opt_to_free = opt;
|
||||
}
|
||||
if (flowlabel)
|
||||
opt = fl6_merge_options(&opt_space, flowlabel, opt);
|
||||
opt = ipv6_fixup_options(&opt_space, opt);
|
||||
|
@ -637,6 +640,7 @@ done:
|
|||
dst_release(dst);
|
||||
out:
|
||||
fl6_sock_release(flowlabel);
|
||||
txopt_put(opt_to_free);
|
||||
|
||||
return err < 0 ? err : len;
|
||||
|
||||
|
|
Loading…
Reference in New Issue