Commit 2423b0d0 authored by David S. Miller's avatar David S. Miller
Browse files

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 5f501532).

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: default avatarDavid S. Miller <davem@davemloft.net>
parents 0807ce0b 2120b7f4
Loading
Loading
Loading
Loading
+11 −9
Original line number Diff line number Diff line
@@ -346,7 +346,6 @@ int aq_ring_rx_clean(struct aq_ring_s *self,
		     int budget)
{
	struct net_device *ndev = aq_nic_get_ndev(self->aq_nic);
	bool is_rsc_completed = true;
	int err = 0;

	for (; (self->sw_head != self->hw_head) && budget;
@@ -364,12 +363,17 @@ int aq_ring_rx_clean(struct aq_ring_s *self,
			continue;

		if (!buff->is_eop) {
			unsigned int frag_cnt = 0U;
			buff_ = buff;
			do {
				bool is_rsc_completed = true;

				if (buff_->next >= self->size) {
					err = -EIO;
					goto err_exit;
				}

				frag_cnt++;
				next_ = buff_->next,
				buff_ = &self->buff_ring[next_];
				is_rsc_completed =
@@ -377,18 +381,17 @@ int aq_ring_rx_clean(struct aq_ring_s *self,
							    next_,
							    self->hw_head);

				if (unlikely(!is_rsc_completed))
					break;
				if (unlikely(!is_rsc_completed) ||
						frag_cnt > MAX_SKB_FRAGS) {
					err = 0;
					goto err_exit;
				}

				buff->is_error |= buff_->is_error;
				buff->is_cso_err |= buff_->is_cso_err;

			} while (!buff_->is_eop);

			if (!is_rsc_completed) {
				err = 0;
				goto err_exit;
			}
			if (buff->is_error ||
			    (buff->is_lro && buff->is_cso_err)) {
				buff_ = buff;
@@ -446,7 +449,7 @@ int aq_ring_rx_clean(struct aq_ring_s *self,
		       ALIGN(hdr_len, sizeof(long)));

		if (buff->len - hdr_len > 0) {
			skb_add_rx_frag(skb, 0, buff->rxdata.page,
			skb_add_rx_frag(skb, i++, buff->rxdata.page,
					buff->rxdata.pg_off + hdr_len,
					buff->len - hdr_len,
					AQ_CFG_RX_FRAME_MAX);
@@ -455,7 +458,6 @@ int aq_ring_rx_clean(struct aq_ring_s *self,

		if (!buff->is_eop) {
			buff_ = buff;
			i = 1U;
			do {
				next_ = buff_->next;
				buff_ = &self->buff_ring[next_];
+7 −0
Original line number Diff line number Diff line
@@ -889,6 +889,13 @@ int hw_atl_b0_hw_ring_tx_head_update(struct aq_hw_s *self,
		err = -ENXIO;
		goto err_exit;
	}

	/* Validate that the new hw_head_ is reasonable. */
	if (hw_head_ >= ring->size) {
		err = -ENXIO;
		goto err_exit;
	}

	ring->hw_head = hw_head_;
	err = aq_hw_err_from_flags(self);