diff options
author | Herbert Xu <herbert@gondor.apana.org.au> | 2006-10-03 17:34:00 -0400 |
---|---|---|
committer | David S. Miller <davem@sunset.davemloft.net> | 2006-10-04 03:30:59 -0400 |
commit | 132a55f3c5c0b1a364d32f65595ad8838c30a60e (patch) | |
tree | 587a0dc800b4f40fc74d3541615d2498cc44c6b8 /net | |
parent | 81771b3b20fb4e98c6f2b2aac2bc10ed41a8f006 (diff) |
[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>
Diffstat (limited to 'net')
-rw-r--r-- | net/ipv6/udp.c | 62 |
1 files changed, 31 insertions, 31 deletions
diff --git a/net/ipv6/udp.c b/net/ipv6/udp.c index 9662561701d1..552ec0f449af 100644 --- a/net/ipv6/udp.c +++ b/net/ipv6/udp.c | |||
@@ -546,7 +546,7 @@ static int udpv6_sendmsg(struct kiocb *iocb, struct sock *sk, | |||
546 | struct in6_addr *daddr, *final_p = NULL, final; | 546 | struct in6_addr *daddr, *final_p = NULL, final; |
547 | struct ipv6_txoptions *opt = NULL; | 547 | struct ipv6_txoptions *opt = NULL; |
548 | struct ip6_flowlabel *flowlabel = NULL; | 548 | struct ip6_flowlabel *flowlabel = NULL; |
549 | struct flowi *fl = &inet->cork.fl; | 549 | struct flowi fl; |
550 | struct dst_entry *dst; | 550 | struct dst_entry *dst; |
551 | int addr_len = msg->msg_namelen; | 551 | int addr_len = msg->msg_namelen; |
552 | int ulen = len; | 552 | int ulen = len; |
@@ -626,19 +626,19 @@ do_udp_sendmsg: | |||
626 | } | 626 | } |
627 | ulen += sizeof(struct udphdr); | 627 | ulen += sizeof(struct udphdr); |
628 | 628 | ||
629 | memset(fl, 0, sizeof(*fl)); | 629 | memset(&fl, 0, sizeof(fl)); |
630 | 630 | ||
631 | if (sin6) { | 631 | if (sin6) { |
632 | if (sin6->sin6_port == 0) | 632 | if (sin6->sin6_port == 0) |
633 | return -EINVAL; | 633 | return -EINVAL; |
634 | 634 | ||
635 | fl->fl_ip_dport = sin6->sin6_port; | 635 | fl.fl_ip_dport = sin6->sin6_port; |
636 | daddr = &sin6->sin6_addr; | 636 | daddr = &sin6->sin6_addr; |
637 | 637 | ||
638 | if (np->sndflow) { | 638 | if (np->sndflow) { |
639 | fl->fl6_flowlabel = sin6->sin6_flowinfo&IPV6_FLOWINFO_MASK; | 639 | fl.fl6_flowlabel = sin6->sin6_flowinfo&IPV6_FLOWINFO_MASK; |
640 | if (fl->fl6_flowlabel&IPV6_FLOWLABEL_MASK) { | 640 | if (fl.fl6_flowlabel&IPV6_FLOWLABEL_MASK) { |
641 | flowlabel = fl6_sock_lookup(sk, fl->fl6_flowlabel); | 641 | flowlabel = fl6_sock_lookup(sk, fl.fl6_flowlabel); |
642 | if (flowlabel == NULL) | 642 | if (flowlabel == NULL) |
643 | return -EINVAL; | 643 | return -EINVAL; |
644 | daddr = &flowlabel->dst; | 644 | daddr = &flowlabel->dst; |
@@ -656,32 +656,32 @@ do_udp_sendmsg: | |||
656 | if (addr_len >= sizeof(struct sockaddr_in6) && | 656 | if (addr_len >= sizeof(struct sockaddr_in6) && |
657 | sin6->sin6_scope_id && | 657 | sin6->sin6_scope_id && |
658 | ipv6_addr_type(daddr)&IPV6_ADDR_LINKLOCAL) | 658 | ipv6_addr_type(daddr)&IPV6_ADDR_LINKLOCAL) |
659 | fl->oif = sin6->sin6_scope_id; | 659 | fl.oif = sin6->sin6_scope_id; |
660 | } else { | 660 | } else { |
661 | if (sk->sk_state != TCP_ESTABLISHED) | 661 | if (sk->sk_state != TCP_ESTABLISHED) |
662 | return -EDESTADDRREQ; | 662 | return -EDESTADDRREQ; |
663 | 663 | ||
664 | fl->fl_ip_dport = inet->dport; | 664 | fl.fl_ip_dport = inet->dport; |
665 | daddr = &np->daddr; | 665 | daddr = &np->daddr; |
666 | fl->fl6_flowlabel = np->flow_label; | 666 | fl.fl6_flowlabel = np->flow_label; |
667 | connected = 1; | 667 | connected = 1; |
668 | } | 668 | } |
669 | 669 | ||
670 | if (!fl->oif) | 670 | if (!fl.oif) |
671 | fl->oif = sk->sk_bound_dev_if; | 671 | fl.oif = sk->sk_bound_dev_if; |
672 | 672 | ||
673 | if (msg->msg_controllen) { | 673 | if (msg->msg_controllen) { |
674 | opt = &opt_space; | 674 | opt = &opt_space; |
675 | memset(opt, 0, sizeof(struct ipv6_txoptions)); | 675 | memset(opt, 0, sizeof(struct ipv6_txoptions)); |
676 | opt->tot_len = sizeof(*opt); | 676 | opt->tot_len = sizeof(*opt); |
677 | 677 | ||
678 | err = datagram_send_ctl(msg, fl, opt, &hlimit, &tclass); | 678 | err = datagram_send_ctl(msg, &fl, opt, &hlimit, &tclass); |
679 | if (err < 0) { | 679 | if (err < 0) { |
680 | fl6_sock_release(flowlabel); | 680 | fl6_sock_release(flowlabel); |
681 | return err; | 681 | return err; |
682 | } | 682 | } |
683 | if ((fl->fl6_flowlabel&IPV6_FLOWLABEL_MASK) && !flowlabel) { | 683 | if ((fl.fl6_flowlabel&IPV6_FLOWLABEL_MASK) && !flowlabel) { |
684 | flowlabel = fl6_sock_lookup(sk, fl->fl6_flowlabel); | 684 | flowlabel = fl6_sock_lookup(sk, fl.fl6_flowlabel); |
685 | if (flowlabel == NULL) | 685 | if (flowlabel == NULL) |
686 | return -EINVAL; | 686 | return -EINVAL; |
687 | } | 687 | } |
@@ -695,39 +695,39 @@ do_udp_sendmsg: | |||
695 | opt = fl6_merge_options(&opt_space, flowlabel, opt); | 695 | opt = fl6_merge_options(&opt_space, flowlabel, opt); |
696 | opt = ipv6_fixup_options(&opt_space, opt); | 696 | opt = ipv6_fixup_options(&opt_space, opt); |
697 | 697 | ||
698 | fl->proto = IPPROTO_UDP; | 698 | fl.proto = IPPROTO_UDP; |
699 | ipv6_addr_copy(&fl->fl6_dst, daddr); | 699 | ipv6_addr_copy(&fl.fl6_dst, daddr); |
700 | if (ipv6_addr_any(&fl->fl6_src) && !ipv6_addr_any(&np->saddr)) | 700 | if (ipv6_addr_any(&fl.fl6_src) && !ipv6_addr_any(&np->saddr)) |
701 | ipv6_addr_copy(&fl->fl6_src, &np->saddr); | 701 | ipv6_addr_copy(&fl.fl6_src, &np->saddr); |
702 | fl->fl_ip_sport = inet->sport; | 702 | fl.fl_ip_sport = inet->sport; |
703 | 703 | ||
704 | /* merge ip6_build_xmit from ip6_output */ | 704 | /* merge ip6_build_xmit from ip6_output */ |
705 | if (opt && opt->srcrt) { | 705 | if (opt && opt->srcrt) { |
706 | struct rt0_hdr *rt0 = (struct rt0_hdr *) opt->srcrt; | 706 | struct rt0_hdr *rt0 = (struct rt0_hdr *) opt->srcrt; |
707 | ipv6_addr_copy(&final, &fl->fl6_dst); | 707 | ipv6_addr_copy(&final, &fl.fl6_dst); |
708 | ipv6_addr_copy(&fl->fl6_dst, rt0->addr); | 708 | ipv6_addr_copy(&fl.fl6_dst, rt0->addr); |
709 | final_p = &final; | 709 | final_p = &final; |
710 | connected = 0; | 710 | connected = 0; |
711 | } | 711 | } |
712 | 712 | ||
713 | if (!fl->oif && ipv6_addr_is_multicast(&fl->fl6_dst)) { | 713 | if (!fl.oif && ipv6_addr_is_multicast(&fl.fl6_dst)) { |
714 | fl->oif = np->mcast_oif; | 714 | fl.oif = np->mcast_oif; |
715 | connected = 0; | 715 | connected = 0; |
716 | } | 716 | } |
717 | 717 | ||
718 | security_sk_classify_flow(sk, fl); | 718 | security_sk_classify_flow(sk, &fl); |
719 | 719 | ||
720 | err = ip6_sk_dst_lookup(sk, &dst, fl); | 720 | err = ip6_sk_dst_lookup(sk, &dst, &fl); |
721 | if (err) | 721 | if (err) |
722 | goto out; | 722 | goto out; |
723 | if (final_p) | 723 | if (final_p) |
724 | ipv6_addr_copy(&fl->fl6_dst, final_p); | 724 | ipv6_addr_copy(&fl.fl6_dst, final_p); |
725 | 725 | ||
726 | if ((err = xfrm_lookup(&dst, fl, sk, 0)) < 0) | 726 | if ((err = xfrm_lookup(&dst, &fl, sk, 0)) < 0) |
727 | goto out; | 727 | goto out; |
728 | 728 | ||
729 | if (hlimit < 0) { | 729 | if (hlimit < 0) { |
730 | if (ipv6_addr_is_multicast(&fl->fl6_dst)) | 730 | if (ipv6_addr_is_multicast(&fl.fl6_dst)) |
731 | hlimit = np->mcast_hops; | 731 | hlimit = np->mcast_hops; |
732 | else | 732 | else |
733 | hlimit = np->hop_limit; | 733 | hlimit = np->hop_limit; |
@@ -763,7 +763,7 @@ back_from_confirm: | |||
763 | do_append_data: | 763 | do_append_data: |
764 | up->len += ulen; | 764 | up->len += ulen; |
765 | err = ip6_append_data(sk, ip_generic_getfrag, msg->msg_iov, ulen, | 765 | err = ip6_append_data(sk, ip_generic_getfrag, msg->msg_iov, ulen, |
766 | sizeof(struct udphdr), hlimit, tclass, opt, fl, | 766 | sizeof(struct udphdr), hlimit, tclass, opt, &fl, |
767 | (struct rt6_info*)dst, | 767 | (struct rt6_info*)dst, |
768 | corkreq ? msg->msg_flags|MSG_MORE : msg->msg_flags); | 768 | corkreq ? msg->msg_flags|MSG_MORE : msg->msg_flags); |
769 | if (err) | 769 | if (err) |
@@ -774,10 +774,10 @@ do_append_data: | |||
774 | if (dst) { | 774 | if (dst) { |
775 | if (connected) { | 775 | if (connected) { |
776 | ip6_dst_store(sk, dst, | 776 | ip6_dst_store(sk, dst, |
777 | ipv6_addr_equal(&fl->fl6_dst, &np->daddr) ? | 777 | ipv6_addr_equal(&fl.fl6_dst, &np->daddr) ? |
778 | &np->daddr : NULL, | 778 | &np->daddr : NULL, |
779 | #ifdef CONFIG_IPV6_SUBTREES | 779 | #ifdef CONFIG_IPV6_SUBTREES |
780 | ipv6_addr_equal(&fl->fl6_src, &np->saddr) ? | 780 | ipv6_addr_equal(&fl.fl6_src, &np->saddr) ? |
781 | &np->saddr : | 781 | &np->saddr : |
782 | #endif | 782 | #endif |
783 | NULL); | 783 | NULL); |