Commit 132a55f3 authored by Herbert Xu's avatar Herbert Xu Committed by David S. Miller

[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: default avatarHerbert Xu <herbert@gondor.apana.org.au>
Acked-by: default avatarJames Morris <jmorris@namei.org>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 81771b3b
...@@ -546,7 +546,7 @@ static int udpv6_sendmsg(struct kiocb *iocb, struct sock *sk, ...@@ -546,7 +546,7 @@ static int udpv6_sendmsg(struct kiocb *iocb, struct sock *sk,
struct in6_addr *daddr, *final_p = NULL, final; struct in6_addr *daddr, *final_p = NULL, final;
struct ipv6_txoptions *opt = NULL; struct ipv6_txoptions *opt = NULL;
struct ip6_flowlabel *flowlabel = NULL; struct ip6_flowlabel *flowlabel = NULL;
struct flowi *fl = &inet->cork.fl; struct flowi fl;
struct dst_entry *dst; struct dst_entry *dst;
int addr_len = msg->msg_namelen; int addr_len = msg->msg_namelen;
int ulen = len; int ulen = len;
...@@ -626,19 +626,19 @@ static int udpv6_sendmsg(struct kiocb *iocb, struct sock *sk, ...@@ -626,19 +626,19 @@ static int udpv6_sendmsg(struct kiocb *iocb, struct sock *sk,
} }
ulen += sizeof(struct udphdr); ulen += sizeof(struct udphdr);
memset(fl, 0, sizeof(*fl)); memset(&fl, 0, sizeof(fl));
if (sin6) { if (sin6) {
if (sin6->sin6_port == 0) if (sin6->sin6_port == 0)
return -EINVAL; return -EINVAL;
fl->fl_ip_dport = sin6->sin6_port; fl.fl_ip_dport = sin6->sin6_port;
daddr = &sin6->sin6_addr; daddr = &sin6->sin6_addr;
if (np->sndflow) { if (np->sndflow) {
fl->fl6_flowlabel = sin6->sin6_flowinfo&IPV6_FLOWINFO_MASK; fl.fl6_flowlabel = sin6->sin6_flowinfo&IPV6_FLOWINFO_MASK;
if (fl->fl6_flowlabel&IPV6_FLOWLABEL_MASK) { if (fl.fl6_flowlabel&IPV6_FLOWLABEL_MASK) {
flowlabel = fl6_sock_lookup(sk, fl->fl6_flowlabel); flowlabel = fl6_sock_lookup(sk, fl.fl6_flowlabel);
if (flowlabel == NULL) if (flowlabel == NULL)
return -EINVAL; return -EINVAL;
daddr = &flowlabel->dst; daddr = &flowlabel->dst;
...@@ -656,32 +656,32 @@ static int udpv6_sendmsg(struct kiocb *iocb, struct sock *sk, ...@@ -656,32 +656,32 @@ static int udpv6_sendmsg(struct kiocb *iocb, struct sock *sk,
if (addr_len >= sizeof(struct sockaddr_in6) && if (addr_len >= sizeof(struct sockaddr_in6) &&
sin6->sin6_scope_id && sin6->sin6_scope_id &&
ipv6_addr_type(daddr)&IPV6_ADDR_LINKLOCAL) ipv6_addr_type(daddr)&IPV6_ADDR_LINKLOCAL)
fl->oif = sin6->sin6_scope_id; fl.oif = sin6->sin6_scope_id;
} else { } else {
if (sk->sk_state != TCP_ESTABLISHED) if (sk->sk_state != TCP_ESTABLISHED)
return -EDESTADDRREQ; return -EDESTADDRREQ;
fl->fl_ip_dport = inet->dport; fl.fl_ip_dport = inet->dport;
daddr = &np->daddr; daddr = &np->daddr;
fl->fl6_flowlabel = np->flow_label; fl.fl6_flowlabel = np->flow_label;
connected = 1; connected = 1;
} }
if (!fl->oif) if (!fl.oif)
fl->oif = sk->sk_bound_dev_if; fl.oif = sk->sk_bound_dev_if;
if (msg->msg_controllen) { if (msg->msg_controllen) {
opt = &opt_space; opt = &opt_space;
memset(opt, 0, sizeof(struct ipv6_txoptions)); memset(opt, 0, sizeof(struct ipv6_txoptions));
opt->tot_len = sizeof(*opt); 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) { if (err < 0) {
fl6_sock_release(flowlabel); fl6_sock_release(flowlabel);
return err; return err;
} }
if ((fl->fl6_flowlabel&IPV6_FLOWLABEL_MASK) && !flowlabel) { if ((fl.fl6_flowlabel&IPV6_FLOWLABEL_MASK) && !flowlabel) {
flowlabel = fl6_sock_lookup(sk, fl->fl6_flowlabel); flowlabel = fl6_sock_lookup(sk, fl.fl6_flowlabel);
if (flowlabel == NULL) if (flowlabel == NULL)
return -EINVAL; return -EINVAL;
} }
...@@ -695,39 +695,39 @@ static int udpv6_sendmsg(struct kiocb *iocb, struct sock *sk, ...@@ -695,39 +695,39 @@ static int udpv6_sendmsg(struct kiocb *iocb, struct sock *sk,
opt = fl6_merge_options(&opt_space, flowlabel, opt); opt = fl6_merge_options(&opt_space, flowlabel, opt);
opt = ipv6_fixup_options(&opt_space, opt); opt = ipv6_fixup_options(&opt_space, opt);
fl->proto = IPPROTO_UDP; fl.proto = IPPROTO_UDP;
ipv6_addr_copy(&fl->fl6_dst, daddr); ipv6_addr_copy(&fl.fl6_dst, daddr);
if (ipv6_addr_any(&fl->fl6_src) && !ipv6_addr_any(&np->saddr)) if (ipv6_addr_any(&fl.fl6_src) && !ipv6_addr_any(&np->saddr))
ipv6_addr_copy(&fl->fl6_src, &np->saddr); ipv6_addr_copy(&fl.fl6_src, &np->saddr);
fl->fl_ip_sport = inet->sport; fl.fl_ip_sport = inet->sport;
/* merge ip6_build_xmit from ip6_output */ /* merge ip6_build_xmit from ip6_output */
if (opt && opt->srcrt) { if (opt && opt->srcrt) {
struct rt0_hdr *rt0 = (struct rt0_hdr *) opt->srcrt; struct rt0_hdr *rt0 = (struct rt0_hdr *) opt->srcrt;
ipv6_addr_copy(&final, &fl->fl6_dst); ipv6_addr_copy(&final, &fl.fl6_dst);
ipv6_addr_copy(&fl->fl6_dst, rt0->addr); ipv6_addr_copy(&fl.fl6_dst, rt0->addr);
final_p = &final; final_p = &final;
connected = 0; connected = 0;
} }
if (!fl->oif && ipv6_addr_is_multicast(&fl->fl6_dst)) { if (!fl.oif && ipv6_addr_is_multicast(&fl.fl6_dst)) {
fl->oif = np->mcast_oif; fl.oif = np->mcast_oif;
connected = 0; 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) if (err)
goto out; goto out;
if (final_p) 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; goto out;
if (hlimit < 0) { if (hlimit < 0) {
if (ipv6_addr_is_multicast(&fl->fl6_dst)) if (ipv6_addr_is_multicast(&fl.fl6_dst))
hlimit = np->mcast_hops; hlimit = np->mcast_hops;
else else
hlimit = np->hop_limit; hlimit = np->hop_limit;
...@@ -763,7 +763,7 @@ static int udpv6_sendmsg(struct kiocb *iocb, struct sock *sk, ...@@ -763,7 +763,7 @@ static int udpv6_sendmsg(struct kiocb *iocb, struct sock *sk,
do_append_data: do_append_data:
up->len += ulen; up->len += ulen;
err = ip6_append_data(sk, ip_generic_getfrag, msg->msg_iov, 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, (struct rt6_info*)dst,
corkreq ? msg->msg_flags|MSG_MORE : msg->msg_flags); corkreq ? msg->msg_flags|MSG_MORE : msg->msg_flags);
if (err) if (err)
...@@ -774,10 +774,10 @@ static int udpv6_sendmsg(struct kiocb *iocb, struct sock *sk, ...@@ -774,10 +774,10 @@ static int udpv6_sendmsg(struct kiocb *iocb, struct sock *sk,
if (dst) { if (dst) {
if (connected) { if (connected) {
ip6_dst_store(sk, dst, ip6_dst_store(sk, dst,
ipv6_addr_equal(&fl->fl6_dst, &np->daddr) ? ipv6_addr_equal(&fl.fl6_dst, &np->daddr) ?
&np->daddr : NULL, &np->daddr : NULL,
#ifdef CONFIG_IPV6_SUBTREES #ifdef CONFIG_IPV6_SUBTREES
ipv6_addr_equal(&fl->fl6_src, &np->saddr) ? ipv6_addr_equal(&fl.fl6_src, &np->saddr) ?
&np->saddr : &np->saddr :
#endif #endif
NULL); NULL);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment