Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

Merge branch 'atlantic-fixes'

Grant Grundler says:

====================
net: atlantic: more fuzzing fixes

It essentially describes four problems:
1) validate rxd_wb->next_desc_ptr before populating buff->next
2) "frag[0] not initialized" case in aq_ring_rx_clean()
3) limit iterations handling fragments in aq_ring_rx_clean()
4) validate hw_head_ in hw_atl_b0_hw_ring_tx_head_update()

(1) was fixed by Zekun Shen <bruceshenzk@gmail.com> around the same time with
"atlantic: Fix buff_ring OOB in aq_ring_rx_clean" (SHA1 5f50153288452e10).

I've added one "clean up" contribution:
"net: atlantic: reduce scope of is_rsc_complete"

I tested the "original" patches using chromeos-v5.4 kernel branch:
https://chromium-review.googlesource.com/q/hashtag:pcinet-atlantic-2022q1+(status:open%20OR%20status:merged)

I've forward ported those patches to 5.18-rc2 and compiled them but am
unable to test them on 5.18-rc2 kernel (logistics problems).

Credit largely goes to ChromeOS Fuzzing team members:
Aashay Shringarpure, Yi Chou, Shervin Oloumi

V2 changes:
o drop first patch - was already fixed upstream differently
o reduce (4) "validate hw_head_" to simple bounds checking.
====================

Signed-off-by: David S. Miller <davem@davemloft.net>

+18 -9
+11 -9
drivers/net/ethernet/aquantia/atlantic/aq_ring.c
··· 346 346 int budget) 347 347 { 348 348 struct net_device *ndev = aq_nic_get_ndev(self->aq_nic); 349 - bool is_rsc_completed = true; 350 349 int err = 0; 351 350 352 351 for (; (self->sw_head != self->hw_head) && budget; ··· 363 364 continue; 364 365 365 366 if (!buff->is_eop) { 367 + unsigned int frag_cnt = 0U; 366 368 buff_ = buff; 367 369 do { 370 + bool is_rsc_completed = true; 371 + 368 372 if (buff_->next >= self->size) { 369 373 err = -EIO; 370 374 goto err_exit; 371 375 } 376 + 377 + frag_cnt++; 372 378 next_ = buff_->next, 373 379 buff_ = &self->buff_ring[next_]; 374 380 is_rsc_completed = ··· 381 377 next_, 382 378 self->hw_head); 383 379 384 - if (unlikely(!is_rsc_completed)) 385 - break; 380 + if (unlikely(!is_rsc_completed) || 381 + frag_cnt > MAX_SKB_FRAGS) { 382 + err = 0; 383 + goto err_exit; 384 + } 386 385 387 386 buff->is_error |= buff_->is_error; 388 387 buff->is_cso_err |= buff_->is_cso_err; 389 388 390 389 } while (!buff_->is_eop); 391 390 392 - if (!is_rsc_completed) { 393 - err = 0; 394 - goto err_exit; 395 - } 396 391 if (buff->is_error || 397 392 (buff->is_lro && buff->is_cso_err)) { 398 393 buff_ = buff; ··· 449 446 ALIGN(hdr_len, sizeof(long))); 450 447 451 448 if (buff->len - hdr_len > 0) { 452 - skb_add_rx_frag(skb, 0, buff->rxdata.page, 449 + skb_add_rx_frag(skb, i++, buff->rxdata.page, 453 450 buff->rxdata.pg_off + hdr_len, 454 451 buff->len - hdr_len, 455 452 AQ_CFG_RX_FRAME_MAX); ··· 458 455 459 456 if (!buff->is_eop) { 460 457 buff_ = buff; 461 - i = 1U; 462 458 do { 463 459 next_ = buff_->next; 464 460 buff_ = &self->buff_ring[next_];
+7
drivers/net/ethernet/aquantia/atlantic/hw_atl/hw_atl_b0.c
··· 889 889 err = -ENXIO; 890 890 goto err_exit; 891 891 } 892 + 893 + /* Validate that the new hw_head_ is reasonable. */ 894 + if (hw_head_ >= ring->size) { 895 + err = -ENXIO; 896 + goto err_exit; 897 + } 898 + 892 899 ring->hw_head = hw_head_; 893 900 err = aq_hw_err_from_flags(self); 894 901