git: 57f5252ff8a1 - main - aq(4): Fix RSS indirection table OOB write and queue distribution

From: Adrian Chadd <adrian_at_FreeBSD.org>
Date: Sat, 20 Jun 2026 19:10:34 UTC
The branch main has been updated by adrian:

URL: https://cgit.FreeBSD.org/src/commit/?id=57f5252ff8a17aa837f677638cce5885b8a5fb3b

commit 57f5252ff8a17aa837f677638cce5885b8a5fb3b
Author:     Nick Price <nick@spun.io>
AuthorDate: 2026-06-20 19:02:22 +0000
Commit:     Adrian Chadd <adrian@FreeBSD.org>
CommitDate: 2026-06-20 19:10:15 +0000

    aq(4): Fix RSS indirection table OOB write and queue distribution
    
    Two related fixes to `aq(4)`'s RSS indirection table handling:
    
    1. Fix an out-of-bounds stack write in `aq_hw_rss_set()`.  RSS table entries are 3 bits (8 queues max), but with more than 8 RX rings `rss_table[]` holds larger values; the 32-bit write then spills one `uint16_t` past `bitary[]` and corrupts the stack, so the NIC never links or the kernel panics.  Mask each value to 3 bits and pack 16 bits at a time to keep the write in bounds.
    
    2. Build the indirection table in `aq_if_attach_post()` with a modulo over `min(rx_rings_count, HW_ATL_RSS_INDIRECTION_QUEUES_MAX)` instead of `i & (rx_rings_count - 1)`, which assumed a power-of-two ring count.
    
    Reviewed by:    adrian
    Differential Revision:  https://reviews.freebsd.org/D57240
---
 sys/dev/aq/aq_hw.c   | 12 ++++++++++--
 sys/dev/aq/aq_hw.h   |  2 ++
 sys/dev/aq/aq_main.c |  3 ++-
 3 files changed, 14 insertions(+), 3 deletions(-)

diff --git a/sys/dev/aq/aq_hw.c b/sys/dev/aq/aq_hw.c
index f73805a939cd..5a45e61041c3 100644
--- a/sys/dev/aq/aq_hw.c
+++ b/sys/dev/aq/aq_hw.c
@@ -878,8 +878,16 @@ aq_hw_rss_set(struct aq_hw_s *self,
 	memset(bitary, 0, sizeof(bitary));
 
 	for (i = HW_ATL_RSS_INDIRECTION_TABLE_MAX; i--;) {
-		(*(uint32_t *)(bitary + ((i * 3U) / 16U))) |=
-			((rss_table[i]) << ((i * 3U) & 0xFU));
+		uint32_t bit_pos = i * HW_ATL_RSS_INDIRECTION_ENTRY_BITS;
+		uint32_t word = bit_pos / 16U;
+		uint32_t shift = bit_pos % 16U;
+		uint32_t field = (uint32_t)(rss_table[i] &
+		    (HW_ATL_RSS_INDIRECTION_QUEUES_MAX - 1U)) << shift;
+
+		bitary[word] |= (uint16_t)field;
+		if (shift + HW_ATL_RSS_INDIRECTION_ENTRY_BITS > 16U &&
+		    word + 1U < ARRAY_SIZE(bitary))
+			bitary[word + 1U] |= (uint16_t)(field >> 16);
 	}
 
 	for (i = ARRAY_SIZE(bitary); i--;) {
diff --git a/sys/dev/aq/aq_hw.h b/sys/dev/aq/aq_hw.h
index a4d4dbb3a512..bdd79871fd76 100644
--- a/sys/dev/aq/aq_hw.h
+++ b/sys/dev/aq/aq_hw.h
@@ -197,6 +197,8 @@ struct aq_hw {
 
 #define HW_ATL_RSS_INDIRECTION_TABLE_MAX  64U
 #define HW_ATL_RSS_HASHKEY_SIZE           40U
+#define HW_ATL_RSS_INDIRECTION_QUEUES_MAX 8U
+#define HW_ATL_RSS_INDIRECTION_ENTRY_BITS 3U
 
 /* PCI core control register */
 #define AQ_HW_PCI_REG_CONTROL_6_ADR 0x1014U
diff --git a/sys/dev/aq/aq_main.c b/sys/dev/aq/aq_main.c
index 2ed9bd050780..e893bb7f73b3 100644
--- a/sys/dev/aq/aq_main.c
+++ b/sys/dev/aq/aq_main.c
@@ -466,8 +466,9 @@ aq_if_attach_post(if_ctx_t ctx)
 	aq_add_stats_sysctls(softc);
 	/* RSS */
 	arc4rand(softc->rss_key, HW_ATL_RSS_HASHKEY_SIZE, 0);
+	uint32_t rss_qs = MIN(softc->rx_rings_count, HW_ATL_RSS_INDIRECTION_QUEUES_MAX);
 	for (int i = ARRAY_SIZE(softc->rss_table); i--;){
-		softc->rss_table[i] = i & (softc->rx_rings_count - 1);
+		softc->rss_table[i] = i % rss_qs;
 	}
 exit:
 	AQ_DBG_EXIT(rc);