CVE-2026-46153
Description
In the Linux kernel, the following vulnerability has been resolved:
8021q: delete cleared egress QoS mappings
vlan_dev_set_egress_priority() currently keeps cleared egress priority mappings in the hash as tombstones. Repeated set/clear cycles with distinct skb priorities therefore accumulate mapping nodes until device teardown and leak memory.
Delete mappings when vlan_prio is cleared instead of keeping tombstones. Now that the egress mapping lists are RCU protected, the node can be unlinked safely and freed after a grace period.
AI Insight
LLM-synthesized narrative grounded in this CVE's description and references.
A memory leak in the Linux kernel's 8021q VLAN module occurs when repeatedly setting and clearing egress QoS mappings, as cleared mappings are kept as tombstones.
Vulnerability
In the Linux kernel's 8021q VLAN module, the function vlan_dev_set_egress_priority() keeps cleared egress priority mappings in the hash as tombstones. This means that when a mapping is cleared (vlan_prio set to zero), the node is not removed from the list. Repeated set/clear cycles with distinct skb priorities accumulate mapping nodes until device teardown, leading to a memory leak. The vulnerability affects all kernel versions that include this code path; the fix is applied in commit a52e122c9e4d [1].
Exploitation
An attacker would need the ability to trigger repeated calls to vlan_dev_set_egress_priority() with distinct skb priorities, followed by clearing those mappings. This could be achieved through local access to a system with VLAN interfaces configured, or via a privileged process that can manipulate egress QoS mappings. No special network position is required beyond local user access that can interact with VLAN device configuration.
Impact
The impact is a memory leak, which over time can exhaust kernel memory, leading to system instability or denial of service. The attacker does not gain code execution or privilege escalation directly, but the leak can degrade system performance and availability.
Mitigation
The fix is included in the Linux kernel stable commit a52e122c9e4d [1]. Users should update to a kernel version containing this commit. As a workaround, avoid repeatedly setting and clearing egress priority mappings on VLAN interfaces. The vulnerability is not listed in CISA's Known Exploited Vulnerabilities (KEV) catalog as of the publication date.
AI Insight generated on May 28, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.
Affected products
2Patches
47dddc74af3698021q: delete cleared egress QoS mappings
2 files changed · +14 −11
net/8021q/vlan_dev.c+14 −6 modifieddiff --git a/net/8021q/vlan_dev.c b/net/8021q/vlan_dev.c index a5340932b657a6..7aa3af8b10ead0 100644 --- a/net/8021q/vlan_dev.c +++ b/net/8021q/vlan_dev.c @@ -172,26 +172,34 @@ int vlan_dev_set_egress_priority(const struct net_device *dev, u32 skb_prio, u16 vlan_prio) { struct vlan_dev_priv *vlan = vlan_dev_priv(dev); + struct vlan_priority_tci_mapping __rcu **mpp; struct vlan_priority_tci_mapping *mp; struct vlan_priority_tci_mapping *np; u32 bucket = skb_prio & 0xF; u32 vlan_qos = (vlan_prio << VLAN_PRIO_SHIFT) & VLAN_PRIO_MASK; /* See if a priority mapping exists.. */ - mp = rtnl_dereference(vlan->egress_priority_map[bucket]); + mpp = &vlan->egress_priority_map[bucket]; + mp = rtnl_dereference(*mpp); while (mp) { if (mp->priority == skb_prio) { - if (mp->vlan_qos && !vlan_qos) + if (!vlan_qos) { + rcu_assign_pointer(*mpp, rtnl_dereference(mp->next)); vlan->nr_egress_mappings--; - else if (!mp->vlan_qos && vlan_qos) - vlan->nr_egress_mappings++; - WRITE_ONCE(mp->vlan_qos, vlan_qos); + kfree_rcu(mp, rcu); + } else { + WRITE_ONCE(mp->vlan_qos, vlan_qos); + } return 0; } - mp = rtnl_dereference(mp->next); + mpp = &mp->next; + mp = rtnl_dereference(*mpp); } /* Create a new mapping then. */ + if (!vlan_qos) + return 0; + np = kmalloc_obj(struct vlan_priority_tci_mapping); if (!np) return -ENOBUFS;
net/8021q/vlan_netlink.c+0 −5 modifieddiff --git a/net/8021q/vlan_netlink.c b/net/8021q/vlan_netlink.c index a5b16833e2ceeb..368d53ca7d8709 100644 --- a/net/8021q/vlan_netlink.c +++ b/net/8021q/vlan_netlink.c @@ -263,10 +263,6 @@ static int vlan_fill_info(struct sk_buff *skb, const struct net_device *dev) for (pm = rcu_dereference_rtnl(vlan->egress_priority_map[i]); pm; pm = rcu_dereference_rtnl(pm->next)) { u16 vlan_qos = READ_ONCE(pm->vlan_qos); - - if (!vlan_qos) - continue; - m.from = pm->priority; m.to = (vlan_qos >> 13) & 0x7; if (nla_put(skb, IFLA_VLAN_QOS_MAPPING, -- cgit 1.3-korg
a52e122c9e4d8021q: delete cleared egress QoS mappings
2 files changed · +14 −11
net/8021q/vlan_dev.c+14 −6 modifieddiff --git a/net/8021q/vlan_dev.c b/net/8021q/vlan_dev.c index a5340932b657a6..7aa3af8b10ead0 100644 --- a/net/8021q/vlan_dev.c +++ b/net/8021q/vlan_dev.c @@ -172,26 +172,34 @@ int vlan_dev_set_egress_priority(const struct net_device *dev, u32 skb_prio, u16 vlan_prio) { struct vlan_dev_priv *vlan = vlan_dev_priv(dev); + struct vlan_priority_tci_mapping __rcu **mpp; struct vlan_priority_tci_mapping *mp; struct vlan_priority_tci_mapping *np; u32 bucket = skb_prio & 0xF; u32 vlan_qos = (vlan_prio << VLAN_PRIO_SHIFT) & VLAN_PRIO_MASK; /* See if a priority mapping exists.. */ - mp = rtnl_dereference(vlan->egress_priority_map[bucket]); + mpp = &vlan->egress_priority_map[bucket]; + mp = rtnl_dereference(*mpp); while (mp) { if (mp->priority == skb_prio) { - if (mp->vlan_qos && !vlan_qos) + if (!vlan_qos) { + rcu_assign_pointer(*mpp, rtnl_dereference(mp->next)); vlan->nr_egress_mappings--; - else if (!mp->vlan_qos && vlan_qos) - vlan->nr_egress_mappings++; - WRITE_ONCE(mp->vlan_qos, vlan_qos); + kfree_rcu(mp, rcu); + } else { + WRITE_ONCE(mp->vlan_qos, vlan_qos); + } return 0; } - mp = rtnl_dereference(mp->next); + mpp = &mp->next; + mp = rtnl_dereference(*mpp); } /* Create a new mapping then. */ + if (!vlan_qos) + return 0; + np = kmalloc_obj(struct vlan_priority_tci_mapping); if (!np) return -ENOBUFS;
net/8021q/vlan_netlink.c+0 −5 modifieddiff --git a/net/8021q/vlan_netlink.c b/net/8021q/vlan_netlink.c index a5b16833e2ceeb..368d53ca7d8709 100644 --- a/net/8021q/vlan_netlink.c +++ b/net/8021q/vlan_netlink.c @@ -263,10 +263,6 @@ static int vlan_fill_info(struct sk_buff *skb, const struct net_device *dev) for (pm = rcu_dereference_rtnl(vlan->egress_priority_map[i]); pm; pm = rcu_dereference_rtnl(pm->next)) { u16 vlan_qos = READ_ONCE(pm->vlan_qos); - - if (!vlan_qos) - continue; - m.from = pm->priority; m.to = (vlan_qos >> 13) & 0x7; if (nla_put(skb, IFLA_VLAN_QOS_MAPPING, -- cgit 1.3-korg
7dddc74af3698021q: delete cleared egress QoS mappings
2 files changed · +14 −11
net/8021q/vlan_dev.c+14 −6 modifieddiff --git a/net/8021q/vlan_dev.c b/net/8021q/vlan_dev.c index a5340932b657a6..7aa3af8b10ead0 100644 --- a/net/8021q/vlan_dev.c +++ b/net/8021q/vlan_dev.c @@ -172,26 +172,34 @@ int vlan_dev_set_egress_priority(const struct net_device *dev, u32 skb_prio, u16 vlan_prio) { struct vlan_dev_priv *vlan = vlan_dev_priv(dev); + struct vlan_priority_tci_mapping __rcu **mpp; struct vlan_priority_tci_mapping *mp; struct vlan_priority_tci_mapping *np; u32 bucket = skb_prio & 0xF; u32 vlan_qos = (vlan_prio << VLAN_PRIO_SHIFT) & VLAN_PRIO_MASK; /* See if a priority mapping exists.. */ - mp = rtnl_dereference(vlan->egress_priority_map[bucket]); + mpp = &vlan->egress_priority_map[bucket]; + mp = rtnl_dereference(*mpp); while (mp) { if (mp->priority == skb_prio) { - if (mp->vlan_qos && !vlan_qos) + if (!vlan_qos) { + rcu_assign_pointer(*mpp, rtnl_dereference(mp->next)); vlan->nr_egress_mappings--; - else if (!mp->vlan_qos && vlan_qos) - vlan->nr_egress_mappings++; - WRITE_ONCE(mp->vlan_qos, vlan_qos); + kfree_rcu(mp, rcu); + } else { + WRITE_ONCE(mp->vlan_qos, vlan_qos); + } return 0; } - mp = rtnl_dereference(mp->next); + mpp = &mp->next; + mp = rtnl_dereference(*mpp); } /* Create a new mapping then. */ + if (!vlan_qos) + return 0; + np = kmalloc_obj(struct vlan_priority_tci_mapping); if (!np) return -ENOBUFS;
net/8021q/vlan_netlink.c+0 −5 modifieddiff --git a/net/8021q/vlan_netlink.c b/net/8021q/vlan_netlink.c index a5b16833e2ceeb..368d53ca7d8709 100644 --- a/net/8021q/vlan_netlink.c +++ b/net/8021q/vlan_netlink.c @@ -263,10 +263,6 @@ static int vlan_fill_info(struct sk_buff *skb, const struct net_device *dev) for (pm = rcu_dereference_rtnl(vlan->egress_priority_map[i]); pm; pm = rcu_dereference_rtnl(pm->next)) { u16 vlan_qos = READ_ONCE(pm->vlan_qos); - - if (!vlan_qos) - continue; - m.from = pm->priority; m.to = (vlan_qos >> 13) & 0x7; if (nla_put(skb, IFLA_VLAN_QOS_MAPPING, -- cgit 1.3-korg
a52e122c9e4d8021q: delete cleared egress QoS mappings
2 files changed · +14 −11
net/8021q/vlan_dev.c+14 −6 modifieddiff --git a/net/8021q/vlan_dev.c b/net/8021q/vlan_dev.c index a5340932b657a6..7aa3af8b10ead0 100644 --- a/net/8021q/vlan_dev.c +++ b/net/8021q/vlan_dev.c @@ -172,26 +172,34 @@ int vlan_dev_set_egress_priority(const struct net_device *dev, u32 skb_prio, u16 vlan_prio) { struct vlan_dev_priv *vlan = vlan_dev_priv(dev); + struct vlan_priority_tci_mapping __rcu **mpp; struct vlan_priority_tci_mapping *mp; struct vlan_priority_tci_mapping *np; u32 bucket = skb_prio & 0xF; u32 vlan_qos = (vlan_prio << VLAN_PRIO_SHIFT) & VLAN_PRIO_MASK; /* See if a priority mapping exists.. */ - mp = rtnl_dereference(vlan->egress_priority_map[bucket]); + mpp = &vlan->egress_priority_map[bucket]; + mp = rtnl_dereference(*mpp); while (mp) { if (mp->priority == skb_prio) { - if (mp->vlan_qos && !vlan_qos) + if (!vlan_qos) { + rcu_assign_pointer(*mpp, rtnl_dereference(mp->next)); vlan->nr_egress_mappings--; - else if (!mp->vlan_qos && vlan_qos) - vlan->nr_egress_mappings++; - WRITE_ONCE(mp->vlan_qos, vlan_qos); + kfree_rcu(mp, rcu); + } else { + WRITE_ONCE(mp->vlan_qos, vlan_qos); + } return 0; } - mp = rtnl_dereference(mp->next); + mpp = &mp->next; + mp = rtnl_dereference(*mpp); } /* Create a new mapping then. */ + if (!vlan_qos) + return 0; + np = kmalloc_obj(struct vlan_priority_tci_mapping); if (!np) return -ENOBUFS;
net/8021q/vlan_netlink.c+0 −5 modifieddiff --git a/net/8021q/vlan_netlink.c b/net/8021q/vlan_netlink.c index a5b16833e2ceeb..368d53ca7d8709 100644 --- a/net/8021q/vlan_netlink.c +++ b/net/8021q/vlan_netlink.c @@ -263,10 +263,6 @@ static int vlan_fill_info(struct sk_buff *skb, const struct net_device *dev) for (pm = rcu_dereference_rtnl(vlan->egress_priority_map[i]); pm; pm = rcu_dereference_rtnl(pm->next)) { u16 vlan_qos = READ_ONCE(pm->vlan_qos); - - if (!vlan_qos) - continue; - m.from = pm->priority; m.to = (vlan_qos >> 13) & 0x7; if (nla_put(skb, IFLA_VLAN_QOS_MAPPING, -- cgit 1.3-korg
Vulnerability mechanics
Root cause
"Missing deletion of egress priority mapping nodes when the VLAN priority is cleared, causing tombstone nodes to accumulate and leak memory."
Attack vector
An attacker with the ability to set and clear VLAN egress priority mappings (e.g., via netlink `IFLA_VLAN_QOS_MAPPING` operations) can repeatedly call `vlan_dev_set_egress_priority()` with distinct `skb_prio` values and a zero `vlan_prio`. Before the fix, each clear operation left a "tombstone" node in the hash list instead of removing it. Repeated set/clear cycles with different `skb_prio` values accumulate these nodes, causing a memory leak that persists until the VLAN device is torn down [patch_id=2898239].
Affected code
The vulnerability is in `net/8021q/vlan_dev.c`, function `vlan_dev_set_egress_priority()`. The egress priority mappings are stored in a hash table (`vlan->egress_priority_map[]`) as a linked list of `vlan_priority_tci_mapping` nodes. A secondary change in `net/8021q/vlan_netlink.c` removes a skip of zero-QoS entries in `vlan_fill_info()`.
What the fix does
The patch modifies `vlan_dev_set_egress_priority()` in `net/8021q/vlan_dev.c` to delete the mapping node when `vlan_prio` is cleared (zero). When a matching node is found and `vlan_qos` is zero, the node is unlinked from the list via `rcu_assign_pointer(*mpp, rtnl_dereference(mp->next))` and freed with `kfree_rcu(mp, rcu)`. Additionally, a new early-return guard (`if (!vlan_qos) return 0;`) prevents allocation of a new node when the caller is trying to clear a non-existent mapping. The change in `net/8021q/vlan_netlink.c` removes the `if (!vlan_qos) continue;` skip, since zero-QoS entries no longer exist in the list [patch_id=2898239].
Preconditions
- authThe attacker must be able to issue netlink commands to set/clear VLAN egress priority mappings on a VLAN netdevice.
- inputThe attacker must be able to specify distinct skb_priority values across repeated operations to accumulate nodes.
Generated on May 28, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
2News mentions
0No linked articles in our index yet.