RE: [Intel-wired-lan] [PATCH net-next v2] e100: fix shift-out-of-bounds in e100_eeprom_load()

"Loktionov, Aleksandr" <[email protected]>
Newsgroups org.kernel.vger.netdev,org.kernel.vger.linux-kernel,org.osuosl.intel-wired-lan
Message-ID <DS4PPF7551E655294090E40920DD86E03A7E5DC2@DS4PPF7551E6552.namprd11.prod.outlook.com>

> -----Original Message-----
> From: Intel-wired-lan <[email protected]> On Behalf
> Of Malathi
> Sent: Tuesday, August 11, 2026 7:08 AM
> To: Nguyen, Anthony L <[email protected]>; Kitszel,
> Przemyslaw <[email protected]>
> Cc: [email protected]; [email protected];
> [email protected]; [email protected]; [email protected];
> [email protected]; [email protected]; linux-
> [email protected]; Malathi <[email protected]>;
> [email protected]
> Subject: [Intel-wired-lan] [PATCH net-next v2] e100: fix shift-out-of-
> bounds in e100_eeprom_load()
> 
> e100_eeprom_load() and e100_eeprom_save() start with an address length
> of 8 and call e100_eeprom_read() to auto-detect the real EEPROM
> address length. e100_eeprom_read() adjusts the length with
> 
> 	*addr_len -= (i - 16);
> 
> based on when the EEPROM drives a dummy zero onto EEDO. A
> malfunctioning or emulated device that drives EEDO low too early makes
> (i - 16) exceed the current length, underflowing the u16 addr_len to a
> large value such as 65529.
> 
> That value is then used as a shift count:
> 
> 	nic->eeprom_wc = 1 << addr_len;
> 
> which is undefined behaviour and additionally overflows the fixed-size
> nic->eeprom[256] cache.
> 
>   UBSAN: shift-out-of-bounds in
> drivers/net/ethernet/intel/e100.c:768:21
>   shift exponent 65529 is too large for 32-bit type 'int'
> 
> The same corrupted addr_len is also fed back into e100_eeprom_read()
> for every subsequent word, where it is used as a shift count again:
> 
> 	cmd_addr_data = ((op_read << *addr_len) | addr) << 16;
> 
> so validating the length only once at the caller is not enough.
> 
> Clamp the length in e100_eeprom_read() so the subtraction can never
> underflow the u16, and reject a zero or out-of-range length in
> e100_eeprom_load() and e100_eeprom_save() before using it. The EEPROM
> cache holds at most 256 words, so a valid address length is in [1, 8].
> 
> Reported-by: [email protected]
> Closes: https://syzkaller.appspot.com/bug?extid=e0abb1d45ac291ebebeb
> Signed-off-by: Malathi <[email protected]>
> ---
> v2:
>  - Drop the Fixes: tag and retarget to net-next; the underflow is only
>    reachable with malfunctioning or emulated hardware, so this is a
>    hardening change and not stable material.
>  - Clamp addr_len inside e100_eeprom_read() so the auto-detect
>    subtraction can never underflow the u16.  The corrupted length was
>    otherwise reused as a shift count for every subsequent word, so
>    validating it only once at the callers (as in v1) was not enough.
>  - Also reject a zero address length in e100_eeprom_load() and
>    e100_eeprom_save().
> 
>  drivers/net/ethernet/intel/e100.c | 17 ++++++++++++++++-
>  1 file changed, 16 insertions(+), 1 deletion(-)
> 
> diff --git a/drivers/net/ethernet/intel/e100.c
> b/drivers/net/ethernet/intel/e100.c
> index 29960762e64a..26a7c0aaa6e2 100644
> --- a/drivers/net/ethernet/intel/e100.c
> +++ b/drivers/net/ethernet/intel/e100.c
> @@ -744,7 +744,12 @@ 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;
> +			else
> +				*addr_len -= len;
>  			i = 17;
>  		}
> 
> @@ -765,6 +770,11 @@ 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);
> +	if (!addr_len || addr_len > 8) {
> +		netif_err(nic, probe, nic->netdev,
> +			  "invalid EEPROM address length %u\n",
> addr_len);
> +		return -EINVAL;
> +	}
>  	nic->eeprom_wc = 1 << addr_len;
> 
>  	for (addr = 0; addr < nic->eeprom_wc; addr++) { @@ -791,6
> +801,11 @@ 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);
> +	if (!addr_len || addr_len > 8) {
> +		netif_err(nic, probe, nic->netdev,
> +			  "invalid EEPROM address length %u\n",
> addr_len);
> +		return -EINVAL;
> +	}
>  	nic->eeprom_wc = 1 << addr_len;
> 
>  	if (start + count >= nic->eeprom_wc)
> --
> 2.43.0

Reviewed-by: Aleksandr Loktionov <[email protected]>
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.