|
@@ -651,8 +651,6 @@ static int rawv6_send_hdrinc(struct sock *sk, struct msghdr *msg, int length,
|
|
|
skb->priority = sk->sk_priority;
|
|
|
skb->mark = sk->sk_mark;
|
|
|
skb->tstamp = sockc->transmit_time;
|
|
|
- skb_dst_set(skb, &rt->dst);
|
|
|
- *dstp = NULL;
|
|
|
|
|
|
skb_put(skb, length);
|
|
|
skb_reset_network_header(skb);
|
|
@@ -665,8 +663,14 @@ static int rawv6_send_hdrinc(struct sock *sk, struct msghdr *msg, int length,
|
|
|
|
|
|
skb->transport_header = skb->network_header;
|
|
|
err = memcpy_from_msg(iph, msg, length);
|
|
|
- if (err)
|
|
|
- goto error_fault;
|
|
|
+ if (err) {
|
|
|
+ err = -EFAULT;
|
|
|
+ kfree_skb(skb);
|
|
|
+ goto error;
|
|
|
+ }
|
|
|
+
|
|
|
+ skb_dst_set(skb, &rt->dst);
|
|
|
+ *dstp = NULL;
|
|
|
|
|
|
/* if egress device is enslaved to an L3 master device pass the
|
|
|
* skb to its handler for processing
|
|
@@ -675,21 +679,28 @@ static int rawv6_send_hdrinc(struct sock *sk, struct msghdr *msg, int length,
|
|
|
if (unlikely(!skb))
|
|
|
return 0;
|
|
|
|
|
|
+ /* Acquire rcu_read_lock() in case we need to use rt->rt6i_idev
|
|
|
+ * in the error path. Since skb has been freed, the dst could
|
|
|
+ * have been queued for deletion.
|
|
|
+ */
|
|
|
+ rcu_read_lock();
|
|
|
IP6_UPD_PO_STATS(net, rt->rt6i_idev, IPSTATS_MIB_OUT, skb->len);
|
|
|
err = NF_HOOK(NFPROTO_IPV6, NF_INET_LOCAL_OUT, net, sk, skb,
|
|
|
NULL, rt->dst.dev, dst_output);
|
|
|
if (err > 0)
|
|
|
err = net_xmit_errno(err);
|
|
|
- if (err)
|
|
|
- goto error;
|
|
|
+ if (err) {
|
|
|
+ IP6_INC_STATS(net, rt->rt6i_idev, IPSTATS_MIB_OUTDISCARDS);
|
|
|
+ rcu_read_unlock();
|
|
|
+ goto error_check;
|
|
|
+ }
|
|
|
+ rcu_read_unlock();
|
|
|
out:
|
|
|
return 0;
|
|
|
|
|
|
-error_fault:
|
|
|
- err = -EFAULT;
|
|
|
- kfree_skb(skb);
|
|
|
error:
|
|
|
IP6_INC_STATS(net, rt->rt6i_idev, IPSTATS_MIB_OUTDISCARDS);
|
|
|
+error_check:
|
|
|
if (err == -ENOBUFS && !np->recverr)
|
|
|
err = 0;
|
|
|
return err;
|