@@ -778,6 +778,26 @@ static unsigned int mergeable_ctx_to_truesize(void *mrg_ctx)
778778 return (unsigned long )mrg_ctx & ((1 << MRG_CTX_HEADER_SHIFT ) - 1 );
779779}
780780
781+ static int check_mergeable_len (struct net_device * dev , void * mrg_ctx ,
782+ unsigned int len )
783+ {
784+ unsigned int headroom , tailroom , room , truesize ;
785+
786+ truesize = mergeable_ctx_to_truesize (mrg_ctx );
787+ headroom = mergeable_ctx_to_headroom (mrg_ctx );
788+ tailroom = headroom ? sizeof (struct skb_shared_info ) : 0 ;
789+ room = SKB_DATA_ALIGN (headroom + tailroom );
790+
791+ if (len > truesize - room ) {
792+ pr_debug ("%s: rx error: len %u exceeds truesize %lu\n" ,
793+ dev -> name , len , (unsigned long )(truesize - room ));
794+ DEV_STATS_INC (dev , rx_length_errors );
795+ return -1 ;
796+ }
797+
798+ return 0 ;
799+ }
800+
781801static struct sk_buff * virtnet_build_skb (void * buf , unsigned int buflen ,
782802 unsigned int headroom ,
783803 unsigned int len )
@@ -1797,7 +1817,8 @@ static unsigned int virtnet_get_headroom(struct virtnet_info *vi)
17971817 * across multiple buffers (num_buf > 1), and we make sure buffers
17981818 * have enough headroom.
17991819 */
1800- static struct page * xdp_linearize_page (struct receive_queue * rq ,
1820+ static struct page * xdp_linearize_page (struct net_device * dev ,
1821+ struct receive_queue * rq ,
18011822 int * num_buf ,
18021823 struct page * p ,
18031824 int offset ,
@@ -1817,18 +1838,27 @@ static struct page *xdp_linearize_page(struct receive_queue *rq,
18171838 memcpy (page_address (page ) + page_off , page_address (p ) + offset , * len );
18181839 page_off += * len ;
18191840
1841+ /* Only mergeable mode can go inside this while loop. In small mode,
1842+ * *num_buf == 1, so it cannot go inside.
1843+ */
18201844 while (-- * num_buf ) {
18211845 unsigned int buflen ;
18221846 void * buf ;
1847+ void * ctx ;
18231848 int off ;
18241849
1825- buf = virtnet_rq_get_buf (rq , & buflen , NULL );
1850+ buf = virtnet_rq_get_buf (rq , & buflen , & ctx );
18261851 if (unlikely (!buf ))
18271852 goto err_buf ;
18281853
18291854 p = virt_to_head_page (buf );
18301855 off = buf - page_address (p );
18311856
1857+ if (check_mergeable_len (dev , ctx , buflen )) {
1858+ put_page (p );
1859+ goto err_buf ;
1860+ }
1861+
18321862 /* guard against a misconfigured or uncooperative backend that
18331863 * is sending packet larger than the MTU.
18341864 */
@@ -1917,7 +1947,7 @@ static struct sk_buff *receive_small_xdp(struct net_device *dev,
19171947 headroom = vi -> hdr_len + header_offset ;
19181948 buflen = SKB_DATA_ALIGN (GOOD_PACKET_LEN + headroom ) +
19191949 SKB_DATA_ALIGN (sizeof (struct skb_shared_info ));
1920- xdp_page = xdp_linearize_page (rq , & num_buf , page ,
1950+ xdp_page = xdp_linearize_page (dev , rq , & num_buf , page ,
19211951 offset , header_offset ,
19221952 & tlen );
19231953 if (!xdp_page )
@@ -2126,10 +2156,9 @@ static int virtnet_build_xdp_buff_mrg(struct net_device *dev,
21262156 struct virtnet_rq_stats * stats )
21272157{
21282158 struct virtio_net_hdr_mrg_rxbuf * hdr = buf ;
2129- unsigned int headroom , tailroom , room ;
2130- unsigned int truesize , cur_frag_size ;
21312159 struct skb_shared_info * shinfo ;
21322160 unsigned int xdp_frags_truesz = 0 ;
2161+ unsigned int truesize ;
21332162 struct page * page ;
21342163 skb_frag_t * frag ;
21352164 int offset ;
@@ -2172,21 +2201,14 @@ static int virtnet_build_xdp_buff_mrg(struct net_device *dev,
21722201 page = virt_to_head_page (buf );
21732202 offset = buf - page_address (page );
21742203
2175- truesize = mergeable_ctx_to_truesize (ctx );
2176- headroom = mergeable_ctx_to_headroom (ctx );
2177- tailroom = headroom ? sizeof (struct skb_shared_info ) : 0 ;
2178- room = SKB_DATA_ALIGN (headroom + tailroom );
2179-
2180- cur_frag_size = truesize ;
2181- xdp_frags_truesz += cur_frag_size ;
2182- if (unlikely (len > truesize - room || cur_frag_size > PAGE_SIZE )) {
2204+ if (check_mergeable_len (dev , ctx , len )) {
21832205 put_page (page );
2184- pr_debug ("%s: rx error: len %u exceeds truesize %lu\n" ,
2185- dev -> name , len , (unsigned long )(truesize - room ));
2186- DEV_STATS_INC (dev , rx_length_errors );
21872206 goto err ;
21882207 }
21892208
2209+ truesize = mergeable_ctx_to_truesize (ctx );
2210+ xdp_frags_truesz += truesize ;
2211+
21902212 frag = & shinfo -> frags [shinfo -> nr_frags ++ ];
21912213 skb_frag_fill_page_desc (frag , page , offset , len );
21922214 if (page_is_pfmemalloc (page ))
@@ -2252,7 +2274,7 @@ static void *mergeable_xdp_get_buf(struct virtnet_info *vi,
22522274 */
22532275 if (!xdp_prog -> aux -> xdp_has_frags ) {
22542276 /* linearize data for XDP */
2255- xdp_page = xdp_linearize_page (rq , num_buf ,
2277+ xdp_page = xdp_linearize_page (vi -> dev , rq , num_buf ,
22562278 * page , offset ,
22572279 XDP_PACKET_HEADROOM ,
22582280 len );
@@ -2400,18 +2422,12 @@ static struct sk_buff *receive_mergeable(struct net_device *dev,
24002422 struct sk_buff * head_skb , * curr_skb ;
24012423 unsigned int truesize = mergeable_ctx_to_truesize (ctx );
24022424 unsigned int headroom = mergeable_ctx_to_headroom (ctx );
2403- unsigned int tailroom = headroom ? sizeof (struct skb_shared_info ) : 0 ;
2404- unsigned int room = SKB_DATA_ALIGN (headroom + tailroom );
24052425
24062426 head_skb = NULL ;
24072427 u64_stats_add (& stats -> bytes , len - vi -> hdr_len );
24082428
2409- if (unlikely (len > truesize - room )) {
2410- pr_debug ("%s: rx error: len %u exceeds truesize %lu\n" ,
2411- dev -> name , len , (unsigned long )(truesize - room ));
2412- DEV_STATS_INC (dev , rx_length_errors );
2429+ if (check_mergeable_len (dev , ctx , len ))
24132430 goto err_skb ;
2414- }
24152431
24162432 if (unlikely (vi -> xdp_enabled )) {
24172433 struct bpf_prog * xdp_prog ;
@@ -2446,17 +2462,10 @@ static struct sk_buff *receive_mergeable(struct net_device *dev,
24462462 u64_stats_add (& stats -> bytes , len );
24472463 page = virt_to_head_page (buf );
24482464
2449- truesize = mergeable_ctx_to_truesize (ctx );
2450- headroom = mergeable_ctx_to_headroom (ctx );
2451- tailroom = headroom ? sizeof (struct skb_shared_info ) : 0 ;
2452- room = SKB_DATA_ALIGN (headroom + tailroom );
2453- if (unlikely (len > truesize - room )) {
2454- pr_debug ("%s: rx error: len %u exceeds truesize %lu\n" ,
2455- dev -> name , len , (unsigned long )(truesize - room ));
2456- DEV_STATS_INC (dev , rx_length_errors );
2465+ if (check_mergeable_len (dev , ctx , len ))
24572466 goto err_skb ;
2458- }
24592467
2468+ truesize = mergeable_ctx_to_truesize (ctx );
24602469 curr_skb = virtnet_skb_append_frag (head_skb , curr_skb , page ,
24612470 buf , len , truesize );
24622471 if (!curr_skb )
0 commit comments