@@ -643,8 +643,6 @@ static int rawv6_send_hdrinc(struct sock *sk, void *from, int length,
643
643
skb -> protocol = htons (ETH_P_IPV6 );
644
644
skb -> priority = sk -> sk_priority ;
645
645
skb -> mark = sk -> sk_mark ;
646
- skb_dst_set (skb , & rt -> dst );
647
- * dstp = NULL ;
648
646
649
647
skb_put (skb , length );
650
648
skb_reset_network_header (skb );
@@ -654,24 +652,37 @@ static int rawv6_send_hdrinc(struct sock *sk, void *from, int length,
654
652
655
653
skb -> transport_header = skb -> network_header ;
656
654
err = memcpy_fromiovecend ((void * )iph , from , 0 , length );
657
- if (err )
658
- goto error_fault ;
655
+ if (err ) {
656
+ err = - EFAULT ;
657
+ kfree_skb (skb );
658
+ goto error ;
659
+ }
659
660
661
+ skb_dst_set (skb , & rt -> dst );
662
+ * dstp = NULL ;
663
+
664
+ /* Acquire rcu_read_lock() in case we need to use rt->rt6i_idev
665
+ * in the error path. Since skb has been freed, the dst could
666
+ * have been queued for deletion.
667
+ */
668
+ rcu_read_lock ();
660
669
IP6_UPD_PO_STATS (net , rt -> rt6i_idev , IPSTATS_MIB_OUT , skb -> len );
661
670
err = NF_HOOK (NFPROTO_IPV6 , NF_INET_LOCAL_OUT , skb , NULL ,
662
671
rt -> dst .dev , dst_output );
663
672
if (err > 0 )
664
673
err = net_xmit_errno (err );
665
- if (err )
666
- goto error ;
674
+ if (err ) {
675
+ IP6_INC_STATS (net , rt -> rt6i_idev , IPSTATS_MIB_OUTDISCARDS );
676
+ rcu_read_unlock ();
677
+ goto error_check ;
678
+ }
679
+ rcu_read_unlock ();
667
680
out :
668
681
return 0 ;
669
682
670
- error_fault :
671
- err = - EFAULT ;
672
- kfree_skb (skb );
673
683
error :
674
684
IP6_INC_STATS (net , rt -> rt6i_idev , IPSTATS_MIB_OUTDISCARDS );
685
+ error_check :
675
686
if (err == - ENOBUFS && !np -> recverr )
676
687
err = 0 ;
677
688
return err ;
0 commit comments