diff options
Diffstat (limited to 'net/ipv6/raw.c')
-rw-r--r-- | net/ipv6/raw.c | 29 |
1 files changed, 20 insertions, 9 deletions
diff --git a/net/ipv6/raw.c b/net/ipv6/raw.c index 413d98bf24f4..5e0efd3954e9 100644 --- a/net/ipv6/raw.c +++ b/net/ipv6/raw.c | |||
@@ -651,8 +651,6 @@ static int rawv6_send_hdrinc(struct sock *sk, struct msghdr *msg, int length, | |||
651 | skb->priority = sk->sk_priority; | 651 | skb->priority = sk->sk_priority; |
652 | skb->mark = sk->sk_mark; | 652 | skb->mark = sk->sk_mark; |
653 | skb->tstamp = sockc->transmit_time; | 653 | skb->tstamp = sockc->transmit_time; |
654 | skb_dst_set(skb, &rt->dst); | ||
655 | *dstp = NULL; | ||
656 | 654 | ||
657 | skb_put(skb, length); | 655 | skb_put(skb, length); |
658 | skb_reset_network_header(skb); | 656 | skb_reset_network_header(skb); |
@@ -665,8 +663,14 @@ static int rawv6_send_hdrinc(struct sock *sk, struct msghdr *msg, int length, | |||
665 | 663 | ||
666 | skb->transport_header = skb->network_header; | 664 | skb->transport_header = skb->network_header; |
667 | err = memcpy_from_msg(iph, msg, length); | 665 | err = memcpy_from_msg(iph, msg, length); |
668 | if (err) | 666 | if (err) { |
669 | goto error_fault; | 667 | err = -EFAULT; |
668 | kfree_skb(skb); | ||
669 | goto error; | ||
670 | } | ||
671 | |||
672 | skb_dst_set(skb, &rt->dst); | ||
673 | *dstp = NULL; | ||
670 | 674 | ||
671 | /* if egress device is enslaved to an L3 master device pass the | 675 | /* if egress device is enslaved to an L3 master device pass the |
672 | * skb to its handler for processing | 676 | * skb to its handler for processing |
@@ -675,21 +679,28 @@ static int rawv6_send_hdrinc(struct sock *sk, struct msghdr *msg, int length, | |||
675 | if (unlikely(!skb)) | 679 | if (unlikely(!skb)) |
676 | return 0; | 680 | return 0; |
677 | 681 | ||
682 | /* Acquire rcu_read_lock() in case we need to use rt->rt6i_idev | ||
683 | * in the error path. Since skb has been freed, the dst could | ||
684 | * have been queued for deletion. | ||
685 | */ | ||
686 | rcu_read_lock(); | ||
678 | IP6_UPD_PO_STATS(net, rt->rt6i_idev, IPSTATS_MIB_OUT, skb->len); | 687 | IP6_UPD_PO_STATS(net, rt->rt6i_idev, IPSTATS_MIB_OUT, skb->len); |
679 | err = NF_HOOK(NFPROTO_IPV6, NF_INET_LOCAL_OUT, net, sk, skb, | 688 | err = NF_HOOK(NFPROTO_IPV6, NF_INET_LOCAL_OUT, net, sk, skb, |
680 | NULL, rt->dst.dev, dst_output); | 689 | NULL, rt->dst.dev, dst_output); |
681 | if (err > 0) | 690 | if (err > 0) |
682 | err = net_xmit_errno(err); | 691 | err = net_xmit_errno(err); |
683 | if (err) | 692 | if (err) { |
684 | goto error; | 693 | IP6_INC_STATS(net, rt->rt6i_idev, IPSTATS_MIB_OUTDISCARDS); |
694 | rcu_read_unlock(); | ||
695 | goto error_check; | ||
696 | } | ||
697 | rcu_read_unlock(); | ||
685 | out: | 698 | out: |
686 | return 0; | 699 | return 0; |
687 | 700 | ||
688 | error_fault: | ||
689 | err = -EFAULT; | ||
690 | kfree_skb(skb); | ||
691 | error: | 701 | error: |
692 | IP6_INC_STATS(net, rt->rt6i_idev, IPSTATS_MIB_OUTDISCARDS); | 702 | IP6_INC_STATS(net, rt->rt6i_idev, IPSTATS_MIB_OUTDISCARDS); |
703 | error_check: | ||
693 | if (err == -ENOBUFS && !np->recverr) | 704 | if (err == -ENOBUFS && !np->recverr) |
694 | err = 0; | 705 | err = 0; |
695 | return err; | 706 | return err; |