FAILED: patch "[PATCH] vlan: fix skb_under_panic and races when toggling HW VLAN" failed to apply to 6.6-stable tree
Subsystems (2)
[email protected]
maintainerThe patch below does not apply to the 6.6-stable tree.
If someone wants it applied there, or to any other stable or longterm tree, then please email the backport, including the original git commit id to <[email protected]>.
To reproduce the conflict and resubmit, you may use the following commands:
git fetch https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/ linux-6.6.y
git checkout FETCH_HEAD
git cherry-pick -x 447cbe95ebb95392b5d8f6a01c0556826919ce23
# <resolve conflicts, build, test, etc.>
git commit -s
git send-email --to '<[email protected]>' --in-reply-to '2026083118-catalog-undercook-0b77@gregkh' --subject-prefix 'PATCH 6.6.y' 'HEAD^..'
Possible dependencies:
thanks,
greg k-h
------------------ original commit in Linus's tree ------------------
From 447cbe95ebb95392b5d8f6a01c0556826919ce23 Mon Sep 17 00:00:00 2001
From: Eric Dumazet <[email protected]>
Date: Tue, 11 Aug 2026 08:52:46 +0000
Subject: [PATCH] vlan: fix skb_under_panic and races when toggling HW VLAN
offload
Toggling hardware VLAN TX offload (NETIF_F_HW_VLAN_CTAG_TX or NETIF_F_HW_VLAN_STAG_TX) on a lower device invokes vlan_transfer_features(), which dynamically changed vlandev->hard_header_len.
This causes two issues:
1. Lockless TX paths (e.g. packet_snd in af_packet.c, ip6_finish_output2) read dev->hard_header_len without holding RTNL lock. Mutating hard_header_len dynamically under RTNL creates a data race where upper layers reserve insufficient headroom based on a stale hard_header_len, resulting in skb_under_panic when vlan_dev_hard_header() is called.
2. In addition, vlan_transfer_features() updated hard_header_len without updating header_ops, causing a mismatch between allocated headroom and header creation.
Always setting dev->hard_header_len = real_dev->hard_header_len and dev->needed_headroom = real_dev->needed_headroom + VLAN_HLEN unconditionally ensures:
- dev->hard_header_len remains 100% static and immutable at real_dev->hard_header_len, eliminating all dynamic runtime updates and data races on hard_header_len.
- Upper layers allocating skbs via LL_RESERVED_SPACE() will always reserve sufficient headroom for software VLAN tag insertion (real_dev->hard_header_len + real_dev->needed_headroom + VLAN_HLEN).
- vlandev inherits real_dev->needed_tailroom so underlying trailer/padding/ICV requirements are honored.
- AF_PACKET SOCK_RAW network header offsets remain correctly aligned at real_dev->hard_header_len.
- vlan_header_ops is used unconditionally.
Note to stable teams: Make sure to backport these commits:
e16e960d55a4 ("ipvlan: inherit needed_headroom and needed_tailroom from phy_dev") cef51860becd ("macvlan: inherit needed_headroom and needed_tailroom from lowerdev")
net/8021q/vlan_dev.c +7 -32
@@ -502,26 +502,6 @@ static const struct header_ops vlan_header_ops = { .parse_protocol = vlan_parse_protocol, }; -static int vlan_passthru_hard_header (struct sk_buff *skb,struct net_device *dev, -unsigned short type, -const void *daddr,const void *saddr, -unsigned int len) -{ -struct vlan_dev_priv *vlan =vlan_dev_priv (dev); -struct net_device *real_dev = vlan->real_dev; - -if (saddr ==NULL ) - saddr = dev->dev_addr; - -return dev_hard_header (skb, real_dev, type, daddr, saddr, len); -} - -static const struct header_ops vlan_passthru_header_ops = { - .create = vlan_passthru_hard_header, - .parse = eth_header_parse, - .parse_protocol = vlan_parse_protocol, -}; -static const struct device_type vlan_type = { .name ="vlan" , };
@@ -580,14 +560,10 @@ static int vlan_dev_init(struct net_device *dev) dev->fcoe_ddp_xid = real_dev->fcoe_ddp_xid;#endif - dev->needed_headroom = real_dev->needed_headroom; -if (vlan_hw_offload_capable (real_dev->features, vlan->vlan_proto)) { - dev->header_ops = &vlan_passthru_header_ops; - dev->hard_header_len = real_dev->hard_header_len; - }else { - dev->header_ops = &vlan_header_ops; - dev->hard_header_len = real_dev->hard_header_len +VLAN_HLEN ; - } + dev->needed_headroom = real_dev->needed_headroom +VLAN_HLEN ; + dev->needed_tailroom = real_dev->needed_tailroom; + dev->header_ops = &vlan_header_ops; + dev->hard_header_len = real_dev->hard_header_len; dev->netdev_ops = &vlan_netdev_ops;
@@ -1029,10 +1005,9 @@ static void vlan_transfer_features(struct net_device *dev,netif_inherit_tso_max (vlandev, dev); -if (vlan_hw_offload_capable (dev->features, vlan->vlan_proto)) - vlandev->hard_header_len = dev->hard_header_len; -else - vlandev->hard_header_len = dev->hard_header_len +VLAN_HLEN ; + vlandev->needed_headroom = dev->needed_headroom +VLAN_HLEN ; + vlandev->needed_tailroom = dev->needed_tailroom; + vlandev->hard_header_len = dev->hard_header_len;#if IS_ENABLED (CONFIG_FCOE ) vlandev->fcoe_ddp_xid = dev->fcoe_ddp_xid;
Sasha Levin
maintainerPATCH 1/2 vlan: defer real device state propagation to netdev_work
From: Jakub Kicinski <[email protected]>
[ Upstream commit cd1c188db1091991fc1d7f565824d077d659425b ]
vlan_device_event() generates nested UP/DOWN, MTU and feature change events. It executes an event for the VLAN device directly from the notifier - while the locks of the lower device are held.
This causes deadlocks, for example:
bond (3) bond_update_speed_duplex(vlan)
| ^ v
vlan (2) UP(vlan) (4) vlan_ethtool_get_link_ksettings()
| ^ v
dummy (1) UP(dummy) (5) __ethtool_get_link_ksettings()
The dummy device is ops locked, vlan creates a nested event (2), then bond wants to ask vlan for link state (3). bond uses the "I'm already holding the instance lock" flavor of API. But in this case the lock held refers to vlan itself. We hit vlan's link settings trampoline (4) and call __ethtool_get_link_ksettings() which tries to lock dummy. Deadlock. There's no clean way for us to tell the vlan_ethtool_get_link_ksettings() that the caller is already in lower device's critical section.
Defer the propagation to the per-netdev work facility instead:
the notifier only schedules netdev_work_sched(vlandev, VLAN_WORK_*), and ndo_work (vlan_dev_work) applies the change later. Hopefully nobody expects the VLAN state changes to be instantaneous.
If someone does expect the changes to be instantaneous we will have to do the same thing Stan did for rx_mode and "strategically" place sync calls, to make sure such delayed works are executed after we drop the ops lock but before we drop rtnl_lock.
Stan suggests that if we need that down the line we may consider reshaping the mechanism into "async notifications". AFAICT only vlan does this sort of netdev open chaining, so as a first try I think that sticking the complexity into the vlan code makes sense.
One corner case is that we need to cancel the event if user explicitly changes the state before work could run. Consider the following operations with vlan0 on top of dummy0:
ip link set dev dummy0 up # queues work to up vlan0 ip link set dev vlan0 down # user explicitly downs the vlan ndo_work # acts on the stale event
[ sashal: Reduced backport -- upstream cd1c188db1091 touches 5 file(s), this
backport carries 3. Not backported here:
Documentation/networking/netdevices.rst
net/core/dev.c
This note is generated from the file lists only; see the resolution record for the reasoning. ]
Stable-dep-of: 447cbe95ebb9 ("vlan: fix skb_under_panic and races when toggling HW VLAN offload")
--- net/8021q/vlan.c | 23 ----------------------- net/8021q/vlan.h | 2 ++ net/8021q/vlan_dev.c | 25 ++++++++++++++++++++++++- 3 files changed, 26 insertions(+), 24 deletions(-)
net/8021q/vlan.c +0 -23
@@ -317,29 +317,6 @@ static void vlan_sync_address(struct net_device *dev,ether_addr_copy (vlan->real_dev_addr, dev->dev_addr); } -static void vlan_transfer_features (struct net_device *dev, -struct net_device *vlandev) -{ -struct vlan_dev_priv *vlan =vlan_dev_priv (vlandev); - -netif_inherit_tso_max (vlandev, dev); - -if (vlan_hw_offload_capable (dev->features, vlan->vlan_proto)) - vlandev->hard_header_len = dev->hard_header_len; -else - vlandev->hard_header_len = dev->hard_header_len +VLAN_HLEN ; - -#if IS_ENABLED (CONFIG_FCOE ) - vlandev->fcoe_ddp_xid = dev->fcoe_ddp_xid; -#endif - - vlandev->priv_flags &= ~IFF_XMIT_DST_RELEASE ; - vlandev->priv_flags |= (vlan->real_dev->priv_flags &IFF_XMIT_DST_RELEASE ); - vlandev->hw_enc_features =vlan_tnl_features (vlan->real_dev); - -netdev_update_features (vlandev); -} -static int __vlan_device_event (struct net_device *dev,unsigned long event) {int err =0 ;
net/8021q/vlan.h +2 -0
@@ -126,6 +126,8 @@ int vlan_filter_push_vids(struct vlan_info *vlan_info, __be16 proto);void vlan_filter_drop_vids (struct vlan_info *vlan_info,__be16 proto);/* found in vlan_dev.c */ +void vlan_transfer_features (struct net_device *dev, +struct net_device *vlandev);void vlan_dev_set_ingress_priority (const struct net_device *dev,u32 skb_prio,u16 vlan_prio); int vlan_dev_set_egress_priority(const struct net_device *dev,
net/8021q/vlan_dev.c +24 -1
@@ -536,7 +536,7 @@ static const struct header_ops vlan_passthru_header_ops = { .parse_protocol = vlan_parse_protocol, }; -static struct device_type vlan_type = { +static const struct device_type vlan_type = { .name ="vlan" , };
@@ -1041,6 +1041,29 @@ static const struct ethtool_ops vlan_ethtool_ops = { .get_ts_info = vlan_ethtool_get_ts_info, }; +void vlan_transfer_features (struct net_device *dev, +struct net_device *vlandev) +{ +struct vlan_dev_priv *vlan =vlan_dev_priv (vlandev); + +netif_inherit_tso_max (vlandev, dev); + +if (vlan_hw_offload_capable (dev->features, vlan->vlan_proto)) + vlandev->hard_header_len = dev->hard_header_len; +else + vlandev->hard_header_len = dev->hard_header_len +VLAN_HLEN ; + +#if IS_ENABLED (CONFIG_FCOE ) + vlandev->fcoe_ddp_xid = dev->fcoe_ddp_xid; +#endif + + vlandev->priv_flags &= ~IFF_XMIT_DST_RELEASE ; + vlandev->priv_flags |= (vlan->real_dev->priv_flags &IFF_XMIT_DST_RELEASE ); + vlandev->hw_enc_features =vlan_tnl_features (vlan->real_dev); + +netdev_update_features (vlandev); +} +static const struct net_device_ops vlan_netdev_ops = { .ndo_change_mtu = vlan_dev_change_mtu, .ndo_init = vlan_dev_init,
Signature · 1 line
2.53.0
Sasha Levin
maintainerPATCH 2/2 vlan: fix skb_under_panic and races when toggling HW VLAN offload
From: Eric Dumazet <[email protected]>
[ Upstream commit 447cbe95ebb95392b5d8f6a01c0556826919ce23 ]
Toggling hardware VLAN TX offload (NETIF_F_HW_VLAN_CTAG_TX or NETIF_F_HW_VLAN_STAG_TX) on a lower device invokes vlan_transfer_features(), which dynamically changed vlandev->hard_header_len.
This causes two issues:
1. Lockless TX paths (e.g. packet_snd in af_packet.c, ip6_finish_output2) read dev->hard_header_len without holding RTNL lock. Mutating hard_header_len dynamically under RTNL creates a data race where upper layers reserve insufficient headroom based on a stale hard_header_len, resulting in skb_under_panic when vlan_dev_hard_header() is called.
2. In addition, vlan_transfer_features() updated hard_header_len without updating header_ops, causing a mismatch between allocated headroom and header creation.
Always setting dev->hard_header_len = real_dev->hard_header_len and dev->needed_headroom = real_dev->needed_headroom + VLAN_HLEN unconditionally ensures:
- dev->hard_header_len remains 100% static and immutable at real_dev->hard_header_len, eliminating all dynamic runtime updates and data races on hard_header_len.
- Upper layers allocating skbs via LL_RESERVED_SPACE() will always reserve sufficient headroom for software VLAN tag insertion (real_dev->hard_header_len + real_dev->needed_headroom + VLAN_HLEN).
- vlandev inherits real_dev->needed_tailroom so underlying trailer/padding/ICV requirements are honored.
- AF_PACKET SOCK_RAW network header offsets remain correctly aligned at real_dev->hard_header_len.
- vlan_header_ops is used unconditionally.
Note to stable teams: Make sure to backport these commits:
e16e960d55a4 ("ipvlan: inherit needed_headroom and needed_tailroom from phy_dev") cef51860becd ("macvlan: inherit needed_headroom and needed_tailroom from lowerdev")
--- net/8021q/vlan_dev.c | 39 +++++++-------------------------------- 1 file changed, 7 insertions(+), 32 deletions(-)
net/8021q/vlan_dev.c +7 -32
@@ -516,26 +516,6 @@ static const struct header_ops vlan_header_ops = { .parse_protocol = vlan_parse_protocol, }; -static int vlan_passthru_hard_header (struct sk_buff *skb,struct net_device *dev, -unsigned short type, -const void *daddr,const void *saddr, -unsigned int len) -{ -struct vlan_dev_priv *vlan =vlan_dev_priv (dev); -struct net_device *real_dev = vlan->real_dev; - -if (saddr ==NULL ) - saddr = dev->dev_addr; - -return dev_hard_header (skb, real_dev, type, daddr, saddr, len); -} - -static const struct header_ops vlan_passthru_header_ops = { - .create = vlan_passthru_hard_header, - .parse = eth_header_parse, - .parse_protocol = vlan_parse_protocol, -}; -static const struct device_type vlan_type = { .name ="vlan" , };
@@ -591,14 +571,10 @@ static int vlan_dev_init(struct net_device *dev) dev->fcoe_ddp_xid = real_dev->fcoe_ddp_xid;#endif - dev->needed_headroom = real_dev->needed_headroom; -if (vlan_hw_offload_capable (real_dev->features, vlan->vlan_proto)) { - dev->header_ops = &vlan_passthru_header_ops; - dev->hard_header_len = real_dev->hard_header_len; - }else { - dev->header_ops = &vlan_header_ops; - dev->hard_header_len = real_dev->hard_header_len +VLAN_HLEN ; - } + dev->needed_headroom = real_dev->needed_headroom +VLAN_HLEN ; + dev->needed_tailroom = real_dev->needed_tailroom; + dev->header_ops = &vlan_header_ops; + dev->hard_header_len = real_dev->hard_header_len; dev->netdev_ops = &vlan_netdev_ops;
@@ -1048,10 +1024,9 @@ void vlan_transfer_features(struct net_device *dev,netif_inherit_tso_max (vlandev, dev); -if (vlan_hw_offload_capable (dev->features, vlan->vlan_proto)) - vlandev->hard_header_len = dev->hard_header_len; -else - vlandev->hard_header_len = dev->hard_header_len +VLAN_HLEN ; + vlandev->needed_headroom = dev->needed_headroom +VLAN_HLEN ; + vlandev->needed_tailroom = dev->needed_tailroom; + vlandev->hard_header_len = dev->hard_header_len;#if IS_ENABLED (CONFIG_FCOE ) vlandev->fcoe_ddp_xid = dev->fcoe_ddp_xid;
Signature · 1 line
2.53.0