[PATCH iwl-next v4] e100: prevent shift-out-of-bounds in EEPROM access

[email protected]
Newsgroups org.osuosl.intel-wired-lan,org.kernel.vger.linux-kernel,org.kernel.vger.netdev
Message-ID <[email protected]>
From: Yalagada Pavan Kumar <[email protected]>

When reading the EEPROM address length, e100_eeprom_read() can return
an invalid length. This value is then used as a shift count when
calculating eeprom_wc, resulting in a shift-out-of-bounds UBSAN warning.

Stop EEPROM address probing if the detected address length exceeds
the initial address length to prevent addr_len from underflowing.

Validate addr_len after reading it from the EEPROM in both
e100_eeprom_load() and e100_eeprom_save(), and return -EIO
if the value is zero or greater than 8.

Reported-by: [email protected]
Closes: https://syzkaller.appspot.com/bug?extid=e0abb1d45ac291ebebeb
Signed-off-by: Yalagada Pavan Kumar <[email protected]>
---

v4:
 - Squash the v1-v3 changes into a single standalone patch.
 - Prevent addr_len underflow during EEPROM probing.
 - Validate addr_len in e100_eeprom_load() and e100_eeprom_save().
 - Use BIT() and cast the result to u16 for eeprom_wc.
 - Target iwl-next as requested by Tony Nguyen.

v3: https://lore.kernel.org/all/[email protected]/T/

v2: https://lore.kernel.org/all/[email protected]/T/

v1: https://lore.kernel.org/all/[email protected]/T/
---
 drivers/net/ethernet/intel/e100.c | 29 ++++++++++++++++++++++++++---
 1 file changed, 26 insertions(+), 3 deletions(-)

diff --git a/drivers/net/ethernet/intel/e100.c b/drivers/net/ethernet/intel/e100.c
index 29960762e64a..cd8190386652 100644
--- a/drivers/net/ethernet/intel/e100.c
+++ b/drivers/net/ethernet/intel/e100.c
@@ -744,7 +744,14 @@ static __le16 e100_eeprom_read(struct nic *nic, u16 *addr_len, u16 addr)
 		 * complete address.  Use this to adjust addr_len. */
 		ctrl = ioread8(&nic->csr->eeprom_ctrl_lo);
 		if (!(ctrl & eedo) && i > 16) {
-			*addr_len -= (i - 16);
+			u16 len = i - 16;
+
+			if (len > *addr_len) {
+				*addr_len = 0;
+				break;
+			}
+
+			*addr_len -= len;
 			i = 17;
 		}
 
@@ -765,7 +772,15 @@ static int e100_eeprom_load(struct nic *nic)
 
 	/* Try reading with an 8-bit addr len to discover actual addr len */
 	e100_eeprom_read(nic, &addr_len, 0);
-	nic->eeprom_wc = 1 << addr_len;
+
+	if (!addr_len || addr_len > 8) {
+		netif_err(nic, probe, nic->netdev,
+			  "Invalid EEPROM address length %u\n",
+			  addr_len);
+		return -EIO;
+	}
+
+	nic->eeprom_wc = (u16)BIT(addr_len);
 
 	for (addr = 0; addr < nic->eeprom_wc; addr++) {
 		nic->eeprom[addr] = e100_eeprom_read(nic, &addr_len, addr);
@@ -791,7 +806,15 @@ static int e100_eeprom_save(struct nic *nic, u16 start, u16 count)
 
 	/* Try reading with an 8-bit addr len to discover actual addr len */
 	e100_eeprom_read(nic, &addr_len, 0);
-	nic->eeprom_wc = 1 << addr_len;
+
+	if (!addr_len || addr_len > 8) {
+		netif_err(nic, probe, nic->netdev,
+			  "Invalid EEPROM address length %u\n",
+			  addr_len);
+		return -EIO;
+	}
+
+	nic->eeprom_wc = (u16)BIT(addr_len);
 
 	if (start + count >= nic->eeprom_wc)
 		return -EINVAL;
-- 
2.43.0
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.