aboutsummaryrefslogtreecommitdiffstats
path: root/net/ipv6/raw.c
diff options
context:
space:
mode:
Diffstat (limited to 'net/ipv6/raw.c')
-rw-r--r--net/ipv6/raw.c29
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();
685out: 698out:
686 return 0; 699 return 0;
687 700
688error_fault:
689 err = -EFAULT;
690 kfree_skb(skb);
691error: 701error:
692 IP6_INC_STATS(net, rt->rt6i_idev, IPSTATS_MIB_OUTDISCARDS); 702 IP6_INC_STATS(net, rt->rt6i_idev, IPSTATS_MIB_OUTDISCARDS);
703error_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;