git: 9d84e9cba12d - stable/15 - igb(4): Fix out-of-bounds register access on VFs

From: Krzysztof Galazka <kgalazka_at_FreeBSD.org>
Date: Fri, 31 Oct 2025 21:07:07 UTC
The branch stable/15 has been updated by kgalazka:

URL: https://cgit.FreeBSD.org/src/commit/?id=9d84e9cba12dad082e0b4bc8dace80a0f09dc950

commit 9d84e9cba12dad082e0b4bc8dace80a0f09dc950
Author:     Krzysztof Galazka <kgalazka@FreeBSD.org>
AuthorDate: 2025-10-24 19:25:47 +0000
Commit:     Krzysztof Galazka <kgalazka@FreeBSD.org>
CommitDate: 2025-10-31 21:06:44 +0000

    igb(4): Fix out-of-bounds register access on VFs
    
    Virtual Functions have access to a limited number of registers,
    and their bus space size is lower. Use KASSERT to detect out-of-bounds
    access and eliminate them to avoid kernel panics in production
    environment.
    
    Signed-off-by: Krzysztof Galazka <krzysztof.galazka@intel.com>
    
    Reviewed by:    jmg
    Tested by:      mateusz.moga_intel.com
    Approved by:    kbowling (mentor), erj (mentor)
    Sponsored by:   Intel Corporation
    Differential Revision:  https://reviews.freebsd.org/D52976
    
    (cherry picked from commit 2c02e6ca7154593d214b62578f67d9fe7db23d70)
---
 sys/dev/e1000/e1000_osdep.h | 46 ++++++++++++++++++-------
 sys/dev/e1000/if_em.c       | 83 +++++++++++++++++++++++++--------------------
 2 files changed, 80 insertions(+), 49 deletions(-)

diff --git a/sys/dev/e1000/e1000_osdep.h b/sys/dev/e1000/e1000_osdep.h
index 893979025f01..ba1c8a16fad1 100644
--- a/sys/dev/e1000/e1000_osdep.h
+++ b/sys/dev/e1000/e1000_osdep.h
@@ -152,6 +152,9 @@ struct e1000_osdep
 {
 	bus_space_tag_t    mem_bus_space_tag;
 	bus_space_handle_t mem_bus_space_handle;
+#ifdef INVARIANTS
+	bus_size_t	   mem_bus_space_size;
+#endif
 	bus_space_tag_t    io_bus_space_tag;
 	bus_space_handle_t io_bus_space_handle;
 	bus_space_tag_t    flash_bus_space_tag;
@@ -175,27 +178,44 @@ struct e1000_osdep
     bus_space_write_4(((struct e1000_osdep *)(hw)->back)->mem_bus_space_tag, \
     ((struct e1000_osdep *)(hw)->back)->mem_bus_space_handle, offset, value)
 
+static __inline uint32_t
+e1000_rd32(struct e1000_osdep *osdep, uint32_t reg)
+{
+
+	KASSERT(reg < osdep->mem_bus_space_size,
+	    ("e1000: register offset %#jx too large (max is %#jx)",
+	    (uintmax_t)reg, (uintmax_t)osdep->mem_bus_space_size));
+
+	return (bus_space_read_4(osdep->mem_bus_space_tag,
+	    osdep->mem_bus_space_handle, reg));
+}
+
+
+static __inline void
+e1000_wr32(struct e1000_osdep *osdep, uint32_t reg, uint32_t value)
+{
+
+	KASSERT(reg < osdep->mem_bus_space_size,
+	    ("e1000: register offset %#jx too large (max is %#jx)",
+	    (uintmax_t)reg, (uintmax_t)osdep->mem_bus_space_size));
+
+	bus_space_write_4(osdep->mem_bus_space_tag,
+	    osdep->mem_bus_space_handle, reg, value);
+}
+
 /* Register READ/WRITE macros */
 
-#define E1000_READ_REG(hw, reg) \
-    bus_space_read_4(((struct e1000_osdep *)(hw)->back)->mem_bus_space_tag, \
-        ((struct e1000_osdep *)(hw)->back)->mem_bus_space_handle, \
-        E1000_REGISTER(hw, reg))
+#define E1000_READ_REG(hw, reg)	\
+	e1000_rd32((hw)->back, E1000_REGISTER(hw, reg))
 
 #define E1000_WRITE_REG(hw, reg, value) \
-    bus_space_write_4(((struct e1000_osdep *)(hw)->back)->mem_bus_space_tag, \
-        ((struct e1000_osdep *)(hw)->back)->mem_bus_space_handle, \
-        E1000_REGISTER(hw, reg), value)
+	e1000_wr32((hw)->back, E1000_REGISTER(hw, reg), value)
 
 #define E1000_READ_REG_ARRAY(hw, reg, index) \
-    bus_space_read_4(((struct e1000_osdep *)(hw)->back)->mem_bus_space_tag, \
-        ((struct e1000_osdep *)(hw)->back)->mem_bus_space_handle, \
-        E1000_REGISTER(hw, reg) + ((index)<< 2))
+        e1000_rd32((hw)->back, E1000_REGISTER(hw, reg) + ((index) << 2))
 
 #define E1000_WRITE_REG_ARRAY(hw, reg, index, value) \
-    bus_space_write_4(((struct e1000_osdep *)(hw)->back)->mem_bus_space_tag, \
-        ((struct e1000_osdep *)(hw)->back)->mem_bus_space_handle, \
-        E1000_REGISTER(hw, reg) + ((index)<< 2), value)
+        e1000_wr32((hw)->back, E1000_REGISTER(hw, reg) + ((index) << 2), value)
 
 #define E1000_READ_REG_ARRAY_DWORD E1000_READ_REG_ARRAY
 #define E1000_WRITE_REG_ARRAY_DWORD E1000_WRITE_REG_ARRAY
diff --git a/sys/dev/e1000/if_em.c b/sys/dev/e1000/if_em.c
index 1f0a2430fa6a..168648c8c5ea 100644
--- a/sys/dev/e1000/if_em.c
+++ b/sys/dev/e1000/if_em.c
@@ -1582,7 +1582,7 @@ em_if_init(if_ctx_t ctx)
 	E1000_WRITE_REG(&sc->hw, E1000_VET, ETHERTYPE_VLAN);
 
 	/* Clear bad data from Rx FIFOs */
-	if (sc->hw.mac.type >= igb_mac_min)
+	if (sc->hw.mac.type >= igb_mac_min && !sc->vf_ifp)
 		e1000_rx_fifo_flush_base(&sc->hw);
 
 	/* Configure for OS presence */
@@ -1602,7 +1602,9 @@ em_if_init(if_ctx_t ctx)
 
 	/* Don't lose promiscuous settings */
 	em_if_set_promisc(ctx, if_getflags(ifp));
-	e1000_clear_hw_cntrs_base_generic(&sc->hw);
+
+	if (sc->hw.mac.ops.clear_hw_cntrs != NULL)
+		sc->hw.mac.ops.clear_hw_cntrs(&sc->hw);
 
 	/* MSI-X configuration for 82574 */
 	if (sc->hw.mac.type == e1000_82574) {
@@ -2349,7 +2351,7 @@ em_if_stop(if_ctx_t ctx)
 		em_flush_desc_rings(sc);
 
 	e1000_reset_hw(&sc->hw);
-	if (sc->hw.mac.type >= e1000_82544)
+	if (sc->hw.mac.type >= e1000_82544 && !sc->vf_ifp)
 		E1000_WRITE_REG(&sc->hw, E1000_WUFC, 0);
 
 	e1000_led_off(&sc->hw);
@@ -2408,6 +2410,9 @@ em_allocate_pci_resources(if_ctx_t ctx)
 	}
 	sc->osdep.mem_bus_space_tag = rman_get_bustag(sc->memory);
 	sc->osdep.mem_bus_space_handle = rman_get_bushandle(sc->memory);
+#ifdef INVARIANTS
+	sc->osdep.mem_bus_space_size = rman_get_size(sc->memory);
+#endif
 	sc->hw.hw_addr = (u8 *)&sc->osdep.mem_bus_space_handle;
 
 	/* Only older adapters use IO mapping */
@@ -3259,11 +3264,13 @@ em_reset(if_ctx_t ctx)
 
 	/* Issue a global reset */
 	e1000_reset_hw(hw);
-	if (hw->mac.type >= igb_mac_min) {
-		E1000_WRITE_REG(hw, E1000_WUC, 0);
-	} else {
-		E1000_WRITE_REG(hw, E1000_WUFC, 0);
-		em_disable_aspm(sc);
+	if (!sc->vf_ifp) {
+		if (hw->mac.type >= igb_mac_min) {
+			E1000_WRITE_REG(hw, E1000_WUC, 0);
+		} else {
+			E1000_WRITE_REG(hw, E1000_WUFC, 0);
+			em_disable_aspm(sc);
+		}
 	}
 	if (sc->flags & IGB_MEDIA_RESET) {
 		e1000_setup_init_funcs(hw, true);
@@ -3813,7 +3820,7 @@ em_initialize_receive_unit(if_ctx_t ctx)
 			    sc->rx_int_delay.value);
 	}
 
-	if (hw->mac.type >= em_mac_min) {
+	if (hw->mac.type >= em_mac_min && !sc->vf_ifp) {
 		uint32_t rfctl;
 		/* Use extended rx descriptor formats */
 		rfctl = E1000_READ_REG(hw, E1000_RFCTL);
@@ -3833,33 +3840,38 @@ em_initialize_receive_unit(if_ctx_t ctx)
 		E1000_WRITE_REG(hw, E1000_RFCTL, rfctl);
 	}
 
-	/* Set up L3 and L4 csum Rx descriptor offloads */
-	rxcsum = E1000_READ_REG(hw, E1000_RXCSUM);
-	if (if_getcapenable(ifp) & IFCAP_RXCSUM) {
-		rxcsum |= E1000_RXCSUM_TUOFL | E1000_RXCSUM_IPOFL;
-		if (hw->mac.type > e1000_82575)
-			rxcsum |= E1000_RXCSUM_CRCOFL;
-		else if (hw->mac.type < em_mac_min &&
-		    if_getcapenable(ifp) & IFCAP_HWCSUM_IPV6)
-			rxcsum |= E1000_RXCSUM_IPV6OFL;
-	} else {
-		rxcsum &= ~(E1000_RXCSUM_IPOFL | E1000_RXCSUM_TUOFL);
-		if (hw->mac.type > e1000_82575)
-			rxcsum &= ~E1000_RXCSUM_CRCOFL;
-		else if (hw->mac.type < em_mac_min)
-			rxcsum &= ~E1000_RXCSUM_IPV6OFL;
-	}
+	/*
+	 * Set up L3 and L4 csum Rx descriptor offloads only on Physical
+	 * Functions. Virtual Functions have no access to this register.
+	 */
+	if (!sc->vf_ifp) {
+		rxcsum = E1000_READ_REG(hw, E1000_RXCSUM);
+		if (if_getcapenable(ifp) & IFCAP_RXCSUM) {
+			rxcsum |= E1000_RXCSUM_TUOFL | E1000_RXCSUM_IPOFL;
+			if (hw->mac.type > e1000_82575)
+				rxcsum |= E1000_RXCSUM_CRCOFL;
+			else if (hw->mac.type < em_mac_min &&
+			    if_getcapenable(ifp) & IFCAP_HWCSUM_IPV6)
+				rxcsum |= E1000_RXCSUM_IPV6OFL;
+		} else {
+			rxcsum &= ~(E1000_RXCSUM_IPOFL | E1000_RXCSUM_TUOFL);
+			if (hw->mac.type > e1000_82575)
+				rxcsum &= ~E1000_RXCSUM_CRCOFL;
+			else if (hw->mac.type < em_mac_min)
+				rxcsum &= ~E1000_RXCSUM_IPV6OFL;
+		}
 
-	if (sc->rx_num_queues > 1) {
-		/* RSS hash needed in the Rx descriptor */
-		rxcsum |= E1000_RXCSUM_PCSD;
+		if (sc->rx_num_queues > 1) {
+			/* RSS hash needed in the Rx descriptor */
+			rxcsum |= E1000_RXCSUM_PCSD;
 
-		if (hw->mac.type >= igb_mac_min)
-			igb_initialize_rss_mapping(sc);
-		else
-			em_initialize_rss_mapping(sc);
+			if (hw->mac.type >= igb_mac_min)
+				igb_initialize_rss_mapping(sc);
+			else
+				em_initialize_rss_mapping(sc);
+		}
+		E1000_WRITE_REG(hw, E1000_RXCSUM, rxcsum);
 	}
-	E1000_WRITE_REG(hw, E1000_RXCSUM, rxcsum);
 
 	for (i = 0, que = sc->rx_queues; i < sc->rx_num_queues; i++, que++) {
 		struct rx_ring *rxr = &que->rxr;
@@ -4367,6 +4379,8 @@ em_get_wakeup(if_ctx_t ctx)
 	switch (sc->hw.mac.type) {
 	case e1000_82542:
 	case e1000_82543:
+	case e1000_vfadapt:
+	case e1000_vfadapt_i350:
 		break;
 	case e1000_82544:
 		e1000_read_nvm(&sc->hw,
@@ -4412,8 +4426,6 @@ em_get_wakeup(if_ctx_t ctx)
 	case e1000_i354:
 	case e1000_i210:
 	case e1000_i211:
-	case e1000_vfadapt:
-	case e1000_vfadapt_i350:
 		apme_mask = E1000_WUC_APME;
 		sc->has_amt = true;
 		eeprom_data = E1000_READ_REG(&sc->hw, E1000_WUC);
@@ -4469,7 +4481,6 @@ em_get_wakeup(if_ctx_t ctx)
 			global_quad_port_a = 0;
 		break;
 	}
-	return;
 }