@@ -651,8 +651,6 @@ static int rawv6_send_hdrinc(struct sock *sk, struct msghdr *msg, int length,
651
651
skb -> priority = sk -> sk_priority ;
652
652
skb -> mark = sk -> sk_mark ;
653
653
skb -> tstamp = sockc -> transmit_time ;
654
- skb_dst_set (skb , & rt -> dst );
655
- * dstp = NULL ;
656
654
657
655
skb_put (skb , length );
658
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
664
skb -> transport_header = skb -> network_header ;
667
665
err = memcpy_from_msg (iph , msg , length );
668
- if (err )
669
- goto error_fault ;
666
+ if (err ) {
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
675
/* if egress device is enslaved to an L3 master device pass the
672
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
679
if (unlikely (!skb ))
676
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
687
IP6_UPD_PO_STATS (net , rt -> rt6i_idev , IPSTATS_MIB_OUT , skb -> len );
679
688
err = NF_HOOK (NFPROTO_IPV6 , NF_INET_LOCAL_OUT , net , sk , skb ,
680
689
NULL , rt -> dst .dev , dst_output );
681
690
if (err > 0 )
682
691
err = net_xmit_errno (err );
683
- if (err )
684
- goto error ;
692
+ if (err ) {
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
698
out :
686
699
return 0 ;
687
700
688
- error_fault :
689
- err = - EFAULT ;
690
- kfree_skb (skb );
691
701
error :
692
702
IP6_INC_STATS (net , rt -> rt6i_idev , IPSTATS_MIB_OUTDISCARDS );
703
+ error_check :
693
704
if (err == - ENOBUFS && !np -> recverr )
694
705
err = 0 ;
695
706
return err ;
0 commit comments