mirror of https://gitee.com/openkylin/linux.git
[UDP6]: Fix flowi clobbering
The udp6_sendmsg function uses a shared buffer to store the flow without taking any locks. This leads to races with SMP. This patch moves the flowi object onto the stack. Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au> Acked-by: James Morris <jmorris@namei.org> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
parent
81771b3b20
commit
132a55f3c5
|
@ -546,7 +546,7 @@ static int udpv6_sendmsg(struct kiocb *iocb, struct sock *sk,
|
|||
struct in6_addr *daddr, *final_p = NULL, final;
|
||||
struct ipv6_txoptions *opt = NULL;
|
||||
struct ip6_flowlabel *flowlabel = NULL;
|
||||
struct flowi *fl = &inet->cork.fl;
|
||||
struct flowi fl;
|
||||
struct dst_entry *dst;
|
||||
int addr_len = msg->msg_namelen;
|
||||
int ulen = len;
|
||||
|
@ -626,19 +626,19 @@ static int udpv6_sendmsg(struct kiocb *iocb, struct sock *sk,
|
|||
}
|
||||
ulen += sizeof(struct udphdr);
|
||||
|
||||
memset(fl, 0, sizeof(*fl));
|
||||
memset(&fl, 0, sizeof(fl));
|
||||
|
||||
if (sin6) {
|
||||
if (sin6->sin6_port == 0)
|
||||
return -EINVAL;
|
||||
|
||||
fl->fl_ip_dport = sin6->sin6_port;
|
||||
fl.fl_ip_dport = sin6->sin6_port;
|
||||
daddr = &sin6->sin6_addr;
|
||||
|
||||
if (np->sndflow) {
|
||||
fl->fl6_flowlabel = sin6->sin6_flowinfo&IPV6_FLOWINFO_MASK;
|
||||
if (fl->fl6_flowlabel&IPV6_FLOWLABEL_MASK) {
|
||||
flowlabel = fl6_sock_lookup(sk, fl->fl6_flowlabel);
|
||||
fl.fl6_flowlabel = sin6->sin6_flowinfo&IPV6_FLOWINFO_MASK;
|
||||
if (fl.fl6_flowlabel&IPV6_FLOWLABEL_MASK) {
|
||||
flowlabel = fl6_sock_lookup(sk, fl.fl6_flowlabel);
|
||||
if (flowlabel == NULL)
|
||||
return -EINVAL;
|
||||
daddr = &flowlabel->dst;
|
||||
|
@ -656,32 +656,32 @@ static int udpv6_sendmsg(struct kiocb *iocb, struct sock *sk,
|
|||
if (addr_len >= sizeof(struct sockaddr_in6) &&
|
||||
sin6->sin6_scope_id &&
|
||||
ipv6_addr_type(daddr)&IPV6_ADDR_LINKLOCAL)
|
||||
fl->oif = sin6->sin6_scope_id;
|
||||
fl.oif = sin6->sin6_scope_id;
|
||||
} else {
|
||||
if (sk->sk_state != TCP_ESTABLISHED)
|
||||
return -EDESTADDRREQ;
|
||||
|
||||
fl->fl_ip_dport = inet->dport;
|
||||
fl.fl_ip_dport = inet->dport;
|
||||
daddr = &np->daddr;
|
||||
fl->fl6_flowlabel = np->flow_label;
|
||||
fl.fl6_flowlabel = np->flow_label;
|
||||
connected = 1;
|
||||
}
|
||||
|
||||
if (!fl->oif)
|
||||
fl->oif = sk->sk_bound_dev_if;
|
||||
if (!fl.oif)
|
||||
fl.oif = sk->sk_bound_dev_if;
|
||||
|
||||
if (msg->msg_controllen) {
|
||||
opt = &opt_space;
|
||||
memset(opt, 0, sizeof(struct ipv6_txoptions));
|
||||
opt->tot_len = sizeof(*opt);
|
||||
|
||||
err = datagram_send_ctl(msg, fl, opt, &hlimit, &tclass);
|
||||
err = datagram_send_ctl(msg, &fl, opt, &hlimit, &tclass);
|
||||
if (err < 0) {
|
||||
fl6_sock_release(flowlabel);
|
||||
return err;
|
||||
}
|
||||
if ((fl->fl6_flowlabel&IPV6_FLOWLABEL_MASK) && !flowlabel) {
|
||||
flowlabel = fl6_sock_lookup(sk, fl->fl6_flowlabel);
|
||||
if ((fl.fl6_flowlabel&IPV6_FLOWLABEL_MASK) && !flowlabel) {
|
||||
flowlabel = fl6_sock_lookup(sk, fl.fl6_flowlabel);
|
||||
if (flowlabel == NULL)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
@ -695,39 +695,39 @@ static int udpv6_sendmsg(struct kiocb *iocb, struct sock *sk,
|
|||
opt = fl6_merge_options(&opt_space, flowlabel, opt);
|
||||
opt = ipv6_fixup_options(&opt_space, opt);
|
||||
|
||||
fl->proto = IPPROTO_UDP;
|
||||
ipv6_addr_copy(&fl->fl6_dst, daddr);
|
||||
if (ipv6_addr_any(&fl->fl6_src) && !ipv6_addr_any(&np->saddr))
|
||||
ipv6_addr_copy(&fl->fl6_src, &np->saddr);
|
||||
fl->fl_ip_sport = inet->sport;
|
||||
fl.proto = IPPROTO_UDP;
|
||||
ipv6_addr_copy(&fl.fl6_dst, daddr);
|
||||
if (ipv6_addr_any(&fl.fl6_src) && !ipv6_addr_any(&np->saddr))
|
||||
ipv6_addr_copy(&fl.fl6_src, &np->saddr);
|
||||
fl.fl_ip_sport = inet->sport;
|
||||
|
||||
/* merge ip6_build_xmit from ip6_output */
|
||||
if (opt && opt->srcrt) {
|
||||
struct rt0_hdr *rt0 = (struct rt0_hdr *) opt->srcrt;
|
||||
ipv6_addr_copy(&final, &fl->fl6_dst);
|
||||
ipv6_addr_copy(&fl->fl6_dst, rt0->addr);
|
||||
ipv6_addr_copy(&final, &fl.fl6_dst);
|
||||
ipv6_addr_copy(&fl.fl6_dst, rt0->addr);
|
||||
final_p = &final;
|
||||
connected = 0;
|
||||
}
|
||||
|
||||
if (!fl->oif && ipv6_addr_is_multicast(&fl->fl6_dst)) {
|
||||
fl->oif = np->mcast_oif;
|
||||
if (!fl.oif && ipv6_addr_is_multicast(&fl.fl6_dst)) {
|
||||
fl.oif = np->mcast_oif;
|
||||
connected = 0;
|
||||
}
|
||||
|
||||
security_sk_classify_flow(sk, fl);
|
||||
security_sk_classify_flow(sk, &fl);
|
||||
|
||||
err = ip6_sk_dst_lookup(sk, &dst, fl);
|
||||
err = ip6_sk_dst_lookup(sk, &dst, &fl);
|
||||
if (err)
|
||||
goto out;
|
||||
if (final_p)
|
||||
ipv6_addr_copy(&fl->fl6_dst, final_p);
|
||||
ipv6_addr_copy(&fl.fl6_dst, final_p);
|
||||
|
||||
if ((err = xfrm_lookup(&dst, fl, sk, 0)) < 0)
|
||||
if ((err = xfrm_lookup(&dst, &fl, sk, 0)) < 0)
|
||||
goto out;
|
||||
|
||||
if (hlimit < 0) {
|
||||
if (ipv6_addr_is_multicast(&fl->fl6_dst))
|
||||
if (ipv6_addr_is_multicast(&fl.fl6_dst))
|
||||
hlimit = np->mcast_hops;
|
||||
else
|
||||
hlimit = np->hop_limit;
|
||||
|
@ -763,7 +763,7 @@ static int udpv6_sendmsg(struct kiocb *iocb, struct sock *sk,
|
|||
do_append_data:
|
||||
up->len += ulen;
|
||||
err = ip6_append_data(sk, ip_generic_getfrag, msg->msg_iov, ulen,
|
||||
sizeof(struct udphdr), hlimit, tclass, opt, fl,
|
||||
sizeof(struct udphdr), hlimit, tclass, opt, &fl,
|
||||
(struct rt6_info*)dst,
|
||||
corkreq ? msg->msg_flags|MSG_MORE : msg->msg_flags);
|
||||
if (err)
|
||||
|
@ -774,10 +774,10 @@ static int udpv6_sendmsg(struct kiocb *iocb, struct sock *sk,
|
|||
if (dst) {
|
||||
if (connected) {
|
||||
ip6_dst_store(sk, dst,
|
||||
ipv6_addr_equal(&fl->fl6_dst, &np->daddr) ?
|
||||
ipv6_addr_equal(&fl.fl6_dst, &np->daddr) ?
|
||||
&np->daddr : NULL,
|
||||
#ifdef CONFIG_IPV6_SUBTREES
|
||||
ipv6_addr_equal(&fl->fl6_src, &np->saddr) ?
|
||||
ipv6_addr_equal(&fl.fl6_src, &np->saddr) ?
|
||||
&np->saddr :
|
||||
#endif
|
||||
NULL);
|
||||
|
|
Loading…
Reference in New Issue