Linux USB
 help / color / mirror / Atom feed
From: sashiko-bot@kernel.org
To: "Birger Koblitz" <mail@birger-koblitz.de>
Cc: linux-usb@vger.kernel.org
Subject: Re: [PATCH net-next v21 07/13] ax88179_178a: Add VLAN offload support for AX88179A
Date: Sun, 15 Nov 2026 10:51:54 +0000	[thread overview]
Message-ID: <sashiko-outbox-215420@kernel.org> (raw)
In-Reply-To: <20261114-ax88179a-v21-7-c2d34410bcbe@birger-koblitz.de>

Thank you for your contribution! Sashiko AI review found 5 potential
issues to consider:
- [Medium] net: usb: ax88179: uninitialized stack read and short USB
  read in VLAN ops
- [Medium] net: usb: ax88179: unchecked USB writes in
  ax88179a_set_features() loop
- [Medium] net: usb: ax88179_178a: TX VLAN tag drop race in
  ax88179a_tx_fixup()
- [Low] net: usb: ax88179_178a: partial feature state in
  ax88179a_set_features()
- [Low] net: usb: ax88179_178a: duplicated VLAN filter update logic in
  add/kill vid
--

commit a2245f1909a4b4c924abeed75400e9c875dcb39c
Author: Birger Koblitz <mail@birger-koblitz.de>

ax88179_178a: Add VLAN offload support for AX88179A

This commit adds hardware VLAN offload and filtering callbacks
(NETIF_F_HW_VLAN_CTAG_TX, NETIF_F_HW_VLAN_CTAG_RX, and
NETIF_F_HW_VLAN_CTAG_FILTER) for AX88179A-based USB network devices.

> diff --git a/drivers/net/usb/ax88179a_devices.c b/drivers/net/usb/ax88179a_devices.c
> index 272e923656832..4207d2cc2b865 100644
> --- a/drivers/net/usb/ax88179a_devices.c
> +++ b/drivers/net/usb/ax88179a_devices.c
> @@ -258,6 +258,88 @@ static const struct ethtool_ops ax88179a_ethtool_ops = {
>  	.get_ts_info		= ethtool_op_get_ts_info,
>  };
>  
> +static int ax88179a_vlan_rx_kill_vid(struct net_device *net, __be16 proto, u16 vid)
> +{
> +	struct usbnet *dev = netdev_priv(net);
> +	u16 reg16 = 0;
> +	u8 vlan_ctrl;
> +	int ret = 0;
> +	u8 reg8 = 0;
> +
> +	ret = ax88179_read_cmd(dev, AX_ACCESS_MAC, AX88179A_VLAN_ID_CONTROL, 1, 1, &reg8);
> +	if (ret < 0)
> +		return ret;
> +	vlan_ctrl = reg8;
> +
> +	/* Address */
> +	reg8 = (vid / 16);
> +	ret = ax88179_write_cmd(dev, AX_ACCESS_MAC, AX88179A_VLAN_ID_ADDRESS, 1, 1, &reg8);
> +	if (ret < 0)
> +		return ret;
> +
> +	/* Data */
> +	reg8 = vlan_ctrl | AX_VLAN_CONTROL_RD;
> +	ret = ax88179_write_cmd(dev, AX_ACCESS_MAC, AX88179A_VLAN_ID_CONTROL, 1, 1, &reg8);
> +	if (ret < 0)
> +		return ret;
> +
> +	ret = ax88179_read_cmd(dev, AX_ACCESS_MAC, AX88179A_VLAN_ID_DATA0, 2, 2, &reg16);
> +	if (ret < 0)
> +		return ret;
> +	reg16 &= ~(1 << (vid % 16));
> +	ret = ax88179_write_cmd(dev, AX_ACCESS_MAC, AX88179A_VLAN_ID_DATA0, 2, 2, &reg16);
> +	if (ret < 0)
> +		return ret;
> +
> +	reg8 = vlan_ctrl | AX_VLAN_CONTROL_WE;
> +	ret = ax88179_write_cmd(dev, AX_ACCESS_MAC, AX88179A_VLAN_ID_CONTROL, 1, 1, &reg8);
> +	if (ret < 0)
> +		return ret;
> +
> +	return 0;
> +}
> +
> +static int ax88179a_vlan_rx_add_vid(struct net_device *net, __be16 proto, u16 vid)
> +{
> +	struct usbnet *dev = netdev_priv(net);
> +	u8 vlan_ctrl;
> +	int ret = 0;
> +	u16 reg16;
> +	u8 reg8;
> +
> +	ret = ax88179_read_cmd(dev, AX_ACCESS_MAC, AX88179A_VLAN_ID_CONTROL, 1, 1, &reg8);
> +	if (ret < 0)
> +		return ret;
> +	vlan_ctrl = reg8;
> +
> +	/* Address */
> +	reg8 = (vid / 16);
> +	ret = ax88179_write_cmd(dev, AX_ACCESS_MAC, AX88179A_VLAN_ID_ADDRESS, 1, 1, &reg8);
> +	if (ret < 0)
> +		return ret;
> +
> +	/* Data */
> +	reg8 = vlan_ctrl | AX_VLAN_CONTROL_RD;
> +	ret = ax88179_write_cmd(dev, AX_ACCESS_MAC, AX88179A_VLAN_ID_CONTROL, 1, 1, &reg8);
> +	if (ret < 0)
> +		return ret;
> +
> +	ret = ax88179_read_cmd(dev, AX_ACCESS_MAC, AX88179A_VLAN_ID_DATA0, 2, 2, &reg16);
> +	if (ret < 0)
> +		return ret;
> +	reg16 |= (1 << (vid % 16));
> +	ret = ax88179_write_cmd(dev, AX_ACCESS_MAC, AX88179A_VLAN_ID_DATA0, 2, 2, &reg16);
> +	if (ret < 0)
> +		return ret;
> +
> +	reg8 = vlan_ctrl | AX_VLAN_CONTROL_WE;
> +	ret = ax88179_write_cmd(dev, AX_ACCESS_MAC, AX88179A_VLAN_ID_CONTROL, 1, 1, &reg8);
> +	if (ret < 0)
> +		return ret;
> +
> +	return 0;
> +}

[Severity: Medium]
Can a 0-byte USB read in ax88179a_vlan_rx_add_vid() or
ax88179a_set_features() write uninitialized stack memory into
AX88179A_VLAN_ID_CONTROL?

For 1-byte reads, ax88179_read_cmd() passes the caller's pointer directly
to __usbnet_read_cmd(), which only copies into the buffer when
usb_control_msg() returns > 0.  If a control read returns 0, reg8 in
ax88179a_vlan_rx_add_vid() and tmp in ax88179a_set_features() remain
uninitialized because the callers only check ret < 0, and that stack byte
is then modified and written back to AX88179A_VLAN_ID_CONTROL.

Similarly, the 2-byte reads of AX88179A_VLAN_ID_DATA0 only check ret < 0
instead of ret < 2, so a short read leaves reg16 zeroed or partially
populated and causes the subsequent write to clear the other 15 VIDs in
the same (vid / 16) filter entry.

[Severity: Low]
This isn't a bug, but ax88179a_vlan_rx_kill_vid() and
ax88179a_vlan_rx_add_vid() duplicate the same five-step register sequence
and only differ in whether they clear or set BIT(vid % 16) in reg16.
Could they share a common helper taking a boolean flag?

[ ... ]

> @@ -517,6 +599,75 @@ static int ax88179a_mii_ioctl(struct net_device *net, struct ifreq *rq, int cmd)
>  	return phylink_mii_ioctl(data->phylink, rq, cmd);
>  }
>  
> +static int ax88179a_set_features(struct net_device *net, netdev_features_t features)
> +{
> +	struct usbnet *dev = netdev_priv(net);
> +	netdev_features_t changed;
> +	int ret;
> +	u8 tmp;
> +
> +	changed = net->features ^ features;
> +
> +	ret = ax88179_set_features(net, features);
> +	if (ret)
> +		return ret;
> +
> +	if (changed & NETIF_F_HW_VLAN_CTAG_FILTER) {
> +		if (features & NETIF_F_HW_VLAN_CTAG_FILTER) {
> +			u8 vlan_state = 0;
> +
> +			ret = ax88179_read_cmd(dev, AX_ACCESS_MAC,
> +					       AX88179A_VLAN_ID_CONTROL, 1, 1, &vlan_state);
> +			if (ret < 0)
> +				return ret;
> +
> +			for (int i = 0; i < 256; i++) {
> +				/* Address */
> +				tmp = i;
> +				ax88179_write_cmd(dev, AX_ACCESS_MAC, AX88179A_VLAN_ID_ADDRESS,
> +						  1, 1, &tmp);
> +				/* Data */
> +				ax88179_write_u16(dev, AX_ACCESS_MAC, AX88179A_VLAN_ID_DATA0,
> +						  2, 0);
> +				/* Write-Execute */
> +				ax88179_write_u8(dev, AX_ACCESS_MAC, AX88179A_VLAN_ID_CONTROL,
> +						 1, vlan_state | AX_VLAN_CONTROL_WE);
> +			}

[Severity: Medium]
Should the USB writes inside this loop check for errors?

If any write fails transiently while the final write enabling
AX_VLAN_CONTROL_VFE succeeds, hardware VLAN filtering is enabled over an
incompletely cleared filter table with stale VID entries.  On a persistent
USB error, the loop also runs all 768 synchronous control transfers under
rtnl_lock instead of bailing out on the first failure.

> +			vlan_state |= AX_VLAN_CONTROL_VFE;
> +			ret = ax88179_write_cmd(dev, AX_ACCESS_MAC,
> +						AX88179A_VLAN_ID_CONTROL, 1, 1, &vlan_state);
> +			if (ret < 0)
> +				return ret;
> +		} else {
> +			ret = ax88179_read_cmd(dev, AX_ACCESS_MAC,
> +					       AX88179A_VLAN_ID_CONTROL, 1, 1, &tmp);
> +			if (ret < 0)
> +				return ret;
> +			tmp &= ~AX_VLAN_CONTROL_VFE;
> +			ret = ax88179_write_cmd(dev, AX_ACCESS_MAC,
> +						AX88179A_VLAN_ID_CONTROL, 1, 1, &tmp);
> +			if (ret < 0)
> +				return ret;
> +		}
> +	}
> +
> +	if (changed & NETIF_F_HW_VLAN_CTAG_RX) {
> +		ret = ax88179_read_cmd(dev, AX_ACCESS_MAC, AX88179A_VLAN_ID_CONTROL,
> +				       1, 1, &tmp);
> +		if (ret < 0)
> +			return ret;
> +		if (features & NETIF_F_HW_VLAN_CTAG_RX)
> +			tmp |= AX_VLAN_CONTROL_VSO;
> +		else
> +			tmp &= ~AX_VLAN_CONTROL_VSO;
> +		ret = ax88179_write_cmd(dev, AX_ACCESS_MAC, AX88179A_VLAN_ID_CONTROL,
> +					1, 1, &tmp);
> +		if (ret < 0)
> +			return ret;
> +	}
> +	return 0;
> +}

[Severity: Low]
Can a late failure in ax88179a_set_features() leave hardware offload state
out of sync with net->features?

When multiple features are changed at once and a later register access
fails, __netdev_update_features() aborts without updating net->features or
repopulating the VLAN filter table, while earlier changes stay active in
hardware.  For example, if enabling NETIF_F_HW_VLAN_CTAG_FILTER succeeds
and the NETIF_F_HW_VLAN_CTAG_RX step fails, AX_VLAN_CONTROL_VFE remains
enabled with an all-zero filter table, and any checksum bits already
toggled via XOR in ax88179_set_features() remain inverted relative to
net->features.

[ ... ]

> @@ -612,9 +766,11 @@ static int ax88179a_bind(struct usbnet *dev, struct usb_interface *intf)
>  		dev->mii.supports_gmii = 1;
>  
>  	dev->net->features |= NETIF_F_SG | NETIF_F_IP_CSUM |
> -			      NETIF_F_IPV6_CSUM | NETIF_F_RXCSUM | NETIF_F_TSO;
> +			      NETIF_F_IPV6_CSUM | NETIF_F_RXCSUM | NETIF_F_TSO |
> +			      NETIF_F_HW_VLAN_CTAG_TX;
>  
>  	dev->net->hw_features |= dev->net->features;
> +	dev->net->hw_features |= NETIF_F_HW_VLAN_CTAG_RX | NETIF_F_HW_VLAN_CTAG_FILTER;

[Severity: Medium]
Can toggling NETIF_F_HW_VLAN_CTAG_TX at runtime cause ax88179a_tx_fixup()
to transmit VLAN-tagged packets without their VLAN tag?

drivers/net/usb/ax88179a_devices.c:ax88179a_tx_fixup() {
    ...
	if ((dev->net->features & NETIF_F_HW_VLAN_CTAG_TX) && (vlan_get_tag(skb, &tci) >= 0)) {
		tx_desc |= AX179A_TX_DESC_VLAN;
		tx_desc |= FIELD_PREP(AX179A_TX_DESC_VLAN_ID, (u64)tci);
	}
    ...
}

CPU0                              CPU1
dev_hard_start_xmit()
  validate_xmit_skb()
    validate_xmit_vlan()
      /* tag left in skb->vlan_tci */
                                  __netdev_update_features()
                                    /* clears NETIF_F_HW_VLAN_CTAG_TX */
  ndo_start_xmit()
    ax88179a_tx_fixup()
      /* skips AX179A_TX_DESC_VLAN */  <-- sent untagged

Now that NETIF_F_HW_VLAN_CTAG_TX is exposed in features and hw_features,
disabling it while packets validated under NETIF_F_HW_VLAN_CTAG_TX are in
flight causes both the feature check in ax88179a_tx_fixup() and
vlan_get_tag() to ignore skb->vlan_tci.  Checking skb_vlan_tag_present(skb)
and reading the tag with skb_vlan_tag_get(skb) avoids dropping the tag.

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20261114-ax88179a-v21-0-c2d34410bcbe@birger-koblitz.de?part=7

  reply	other threads:[~2026-11-15 10:51 UTC|newest]

Thread overview: 27+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-11-14 10:50 [PATCH net-next v21 00/13] ax88179_178a: Add support for AX88179A-based chips Birger Koblitz
2026-11-14 10:50 ` [PATCH net-next v21 01/13] phylink: Add phylink_mac_interrupt Birger Koblitz
2026-11-15 10:51   ` sashiko-bot
2026-11-14 10:50 ` [PATCH net-next v21 02/13] phylib: Add support for PHYs with broken forced mode Birger Koblitz
2026-11-15 10:51   ` sashiko-bot
2026-11-14 10:50 ` [PATCH net-next v21 03/13] ax88179_178a: Split driver into library and device specific code Birger Koblitz
2026-11-15 10:51   ` sashiko-bot
2026-11-14 10:51 ` [PATCH net-next v21 04/13] ax88179_178a: Add HW support for AX179A-based chips Birger Koblitz
2026-11-15 10:51   ` sashiko-bot
2026-11-14 10:51 ` [PATCH net-next v21 05/13] ax88179_178a: Add EEE configuration support for AX88179A MACs Birger Koblitz
2026-11-15 10:51   ` sashiko-bot
2026-11-14 10:51 ` [PATCH net-next v21 06/13] ax88179_178a: Add EEE configuration support for AX88179A PHYs Birger Koblitz
2026-11-15 10:51   ` sashiko-bot
2026-11-14 10:51 ` [PATCH net-next v21 07/13] ax88179_178a: Add VLAN offload support for AX88179A Birger Koblitz
2026-11-15 10:51   ` sashiko-bot [this message]
2026-11-14 10:51 ` [PATCH net-next v21 08/13] ax88179_178a: Add AX179A/AX279 multicast configuration Birger Koblitz
2026-11-15 10:51   ` sashiko-bot
2026-11-14 10:51 ` [PATCH net-next v21 09/13] ax88179_178a: Add Suspend/resume support for AX88179A/772D/279 Birger Koblitz
2026-11-15 10:51   ` sashiko-bot
2026-11-14 10:51 ` [PATCH net-next v21 10/13] ax88179_178a: Add ethtool get_drvinfo Birger Koblitz
2026-11-15 10:51   ` sashiko-bot
2026-11-14 10:51 ` [PATCH net-next v21 11/13] ax88179_178a: Update driver name and information Birger Koblitz
2026-11-15 10:51   ` sashiko-bot
2026-11-14 10:51 ` [PATCH net-next v21 12/13] ax88179_178a: Add support for AX88179A/772D/279 EEPROM access Birger Koblitz
2026-11-15 10:51   ` sashiko-bot
2026-11-14 10:51 ` [PATCH net-next v21 13/13] ax88796b: Add support for AX88772D, AX88179A and AX88279 Birger Koblitz
2026-11-15 10:51   ` sashiko-bot

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=sashiko-outbox-215420@kernel.org \
    --to=sashiko-bot@kernel.org \
    --cc=linux-usb@vger.kernel.org \
    --cc=mail@birger-koblitz.de \
    --cc=sashiko-reviews@lists.linux.dev \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox