CVE-2026-46165
Description
In the Linux kernel, the following vulnerability has been resolved:
openvswitch: vport: fix self-deadlock on release of tunnel ports
vports are used concurrently and protected by RCU, so netdev_put() must happen after the RCU grace period. So, either in an RCU call or after the synchronize_net(). The rtnl_delete_link() must happen under RTNL and so can't be executed in RCU context. Calling synchronize_net() while holding RTNL is not a good idea for performance and system stability under load in general, so calling netdev_put() in RCU call is the right solution here.
However, when the device is deleted, rtnl_unlock() will call netdev_run_todo() and block until all the references are gone. In the current code this means that we never reach the call_rcu() and the vport is never freed and the reference is never released, causing a self-deadlock on device removal.
Fix that by moving the rcu_call() before the rtnl_unlock(), so the scheduled RCU callback will be executed when synchronize_net() is called from the rtnl_unlock()->netdev_run_todo() while the RTNL itself is already released.
AI Insight
LLM-synthesized narrative grounded in this CVE's description and references.
Linux kernel openvswitch tunnel port release can self-deadlock because netdev_put() is delayed past rtnl_unlock(), blocking device removal.
Vulnerability
In the Linux kernel's openvswitch module, a deadlock occurs during the release of tunnel ports. The vport (virtual port) for tunnel devices is accessed concurrently under RCU protection. The current code calls netdev_put() via rcu_call() only after rtnl_unlock(), but the device deletion path in rtnl_unlock() → netdev_run_todo() blocks until all references are gone. This creates a self-deadlock: the rcu_call() never executes, the reference is never released, and the device removal hangs. Affected versions include all kernels prior to the patch introduced by commit c741433f6c8d[1].
Exploitation
An attacker with the ability to configure network namespaces and create tunnel vports (e.g., using ovs-vsctl) can trigger the deadlock by deleting the tunnel port while it is in use. No special privileges beyond the ability to manage Open vSwitch are required. The exploitation is straightforward: create a tunnel port and then delete it; the kernel will deadlock on rtnl_unlock(), causing a hang or denial of service.
Impact
A denial of service (DoS) occurs: the kernel hangs indefinitely during the tunnel port deletion, making the system unresponsive. This affects availability but does not directly lead to information disclosure or privilege escalation. The scope is local to the system where the tunnel port is deleted.
Mitigation
A fix is available in the Linux kernel stable tree as commit c741433f6c8dcdecd1d9549d89053761fd1ea413, which moves rcu_call() before rtnl_unlock() so that the RCU callback runs during synchronize_net() inside netdev_run_todo()[1]. Users should update to a kernel containing this patch. No workaround is documented.
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
1Patches
103df75fff46b1openvswitch: vport: fix self-deadlock on release of tunnel ports
1 file changed · +5 −2
net/openvswitch/vport-netdev.c+5 −2 modifieddiff --git a/net/openvswitch/vport-netdev.c b/net/openvswitch/vport-netdev.c index 12055af832dc08..a1df551e915bca 100644 --- a/net/openvswitch/vport-netdev.c +++ b/net/openvswitch/vport-netdev.c @@ -196,9 +196,13 @@ void ovs_netdev_tunnel_destroy(struct vport *vport) */ if (vport->dev->reg_state == NETREG_REGISTERED) rtnl_delete_link(vport->dev, 0, NULL); - rtnl_unlock(); + /* We can't put the device reference yet, since it can still be in + * use, but rtnl_unlock()->netdev_run_todo() will block until all + * the references are released, so the RCU call must be before it. + */ call_rcu(&vport->rcu, vport_netdev_free); + rtnl_unlock(); } EXPORT_SYMBOL_GPL(ovs_netdev_tunnel_destroy); -- cgit 1.3-korg
c741433f6c8dopenvswitch: vport: fix self-deadlock on release of tunnel ports
1 file changed · +5 −2
net/openvswitch/vport-netdev.c+5 −2 modifieddiff --git a/net/openvswitch/vport-netdev.c b/net/openvswitch/vport-netdev.c index 68d38c12427c1e..ca9cee48d1525c 100644 --- a/net/openvswitch/vport-netdev.c +++ b/net/openvswitch/vport-netdev.c @@ -189,9 +189,13 @@ void ovs_netdev_tunnel_destroy(struct vport *vport) */ if (vport->dev->reg_state == NETREG_REGISTERED) rtnl_delete_link(vport->dev, 0, NULL); - rtnl_unlock(); + /* We can't put the device reference yet, since it can still be in + * use, but rtnl_unlock()->netdev_run_todo() will block until all + * the references are released, so the RCU call must be before it. + */ call_rcu(&vport->rcu, vport_netdev_free); + rtnl_unlock(); } EXPORT_SYMBOL_GPL(ovs_netdev_tunnel_destroy); -- cgit 1.3-korg
6522d59fb7deopenvswitch: vport: fix self-deadlock on release of tunnel ports
1 file changed · +5 −2
net/openvswitch/vport-netdev.c+5 −2 modifieddiff --git a/net/openvswitch/vport-netdev.c b/net/openvswitch/vport-netdev.c index 12055af832dc08..a1df551e915bca 100644 --- a/net/openvswitch/vport-netdev.c +++ b/net/openvswitch/vport-netdev.c @@ -196,9 +196,13 @@ void ovs_netdev_tunnel_destroy(struct vport *vport) */ if (vport->dev->reg_state == NETREG_REGISTERED) rtnl_delete_link(vport->dev, 0, NULL); - rtnl_unlock(); + /* We can't put the device reference yet, since it can still be in + * use, but rtnl_unlock()->netdev_run_todo() will block until all + * the references are released, so the RCU call must be before it. + */ call_rcu(&vport->rcu, vport_netdev_free); + rtnl_unlock(); } EXPORT_SYMBOL_GPL(ovs_netdev_tunnel_destroy); -- cgit 1.3-korg
366c482965c6openvswitch: vport: fix self-deadlock on release of tunnel ports
1 file changed · +5 −2
net/openvswitch/vport-netdev.c+5 −2 modifieddiff --git a/net/openvswitch/vport-netdev.c b/net/openvswitch/vport-netdev.c index 12055af832dc08..a1df551e915bca 100644 --- a/net/openvswitch/vport-netdev.c +++ b/net/openvswitch/vport-netdev.c @@ -196,9 +196,13 @@ void ovs_netdev_tunnel_destroy(struct vport *vport) */ if (vport->dev->reg_state == NETREG_REGISTERED) rtnl_delete_link(vport->dev, 0, NULL); - rtnl_unlock(); + /* We can't put the device reference yet, since it can still be in + * use, but rtnl_unlock()->netdev_run_todo() will block until all + * the references are released, so the RCU call must be before it. + */ call_rcu(&vport->rcu, vport_netdev_free); + rtnl_unlock(); } EXPORT_SYMBOL_GPL(ovs_netdev_tunnel_destroy); -- cgit 1.3-korg
aa69918bd418openvswitch: vport: fix self-deadlock on release of tunnel ports
1 file changed · +5 −2
net/openvswitch/vport-netdev.c+5 −2 modifieddiff --git a/net/openvswitch/vport-netdev.c b/net/openvswitch/vport-netdev.c index a92ca8b37f96ab..c42642075685d7 100644 --- a/net/openvswitch/vport-netdev.c +++ b/net/openvswitch/vport-netdev.c @@ -208,9 +208,13 @@ void ovs_netdev_tunnel_destroy(struct vport *vport) */ if (vport->dev->reg_state == NETREG_REGISTERED) rtnl_delete_link(vport->dev, 0, NULL); - rtnl_unlock(); + /* We can't put the device reference yet, since it can still be in + * use, but rtnl_unlock()->netdev_run_todo() will block until all + * the references are released, so the RCU call must be before it. + */ call_rcu(&vport->rcu, vport_netdev_free); + rtnl_unlock(); } EXPORT_SYMBOL_GPL(ovs_netdev_tunnel_destroy); -- cgit 1.3-korg
c741433f6c8dopenvswitch: vport: fix self-deadlock on release of tunnel ports
1 file changed · +5 −2
net/openvswitch/vport-netdev.c+5 −2 modifieddiff --git a/net/openvswitch/vport-netdev.c b/net/openvswitch/vport-netdev.c index 68d38c12427c1e..ca9cee48d1525c 100644 --- a/net/openvswitch/vport-netdev.c +++ b/net/openvswitch/vport-netdev.c @@ -189,9 +189,13 @@ void ovs_netdev_tunnel_destroy(struct vport *vport) */ if (vport->dev->reg_state == NETREG_REGISTERED) rtnl_delete_link(vport->dev, 0, NULL); - rtnl_unlock(); + /* We can't put the device reference yet, since it can still be in + * use, but rtnl_unlock()->netdev_run_todo() will block until all + * the references are released, so the RCU call must be before it. + */ call_rcu(&vport->rcu, vport_netdev_free); + rtnl_unlock(); } EXPORT_SYMBOL_GPL(ovs_netdev_tunnel_destroy); -- cgit 1.3-korg
366c482965c6openvswitch: vport: fix self-deadlock on release of tunnel ports
1 file changed · +5 −2
net/openvswitch/vport-netdev.c+5 −2 modifieddiff --git a/net/openvswitch/vport-netdev.c b/net/openvswitch/vport-netdev.c index 12055af832dc08..a1df551e915bca 100644 --- a/net/openvswitch/vport-netdev.c +++ b/net/openvswitch/vport-netdev.c @@ -196,9 +196,13 @@ void ovs_netdev_tunnel_destroy(struct vport *vport) */ if (vport->dev->reg_state == NETREG_REGISTERED) rtnl_delete_link(vport->dev, 0, NULL); - rtnl_unlock(); + /* We can't put the device reference yet, since it can still be in + * use, but rtnl_unlock()->netdev_run_todo() will block until all + * the references are released, so the RCU call must be before it. + */ call_rcu(&vport->rcu, vport_netdev_free); + rtnl_unlock(); } EXPORT_SYMBOL_GPL(ovs_netdev_tunnel_destroy); -- cgit 1.3-korg
3df75fff46b1openvswitch: vport: fix self-deadlock on release of tunnel ports
1 file changed · +5 −2
net/openvswitch/vport-netdev.c+5 −2 modifieddiff --git a/net/openvswitch/vport-netdev.c b/net/openvswitch/vport-netdev.c index 12055af832dc08..a1df551e915bca 100644 --- a/net/openvswitch/vport-netdev.c +++ b/net/openvswitch/vport-netdev.c @@ -196,9 +196,13 @@ void ovs_netdev_tunnel_destroy(struct vport *vport) */ if (vport->dev->reg_state == NETREG_REGISTERED) rtnl_delete_link(vport->dev, 0, NULL); - rtnl_unlock(); + /* We can't put the device reference yet, since it can still be in + * use, but rtnl_unlock()->netdev_run_todo() will block until all + * the references are released, so the RCU call must be before it. + */ call_rcu(&vport->rcu, vport_netdev_free); + rtnl_unlock(); } EXPORT_SYMBOL_GPL(ovs_netdev_tunnel_destroy); -- cgit 1.3-korg
6522d59fb7deopenvswitch: vport: fix self-deadlock on release of tunnel ports
1 file changed · +5 −2
net/openvswitch/vport-netdev.c+5 −2 modifieddiff --git a/net/openvswitch/vport-netdev.c b/net/openvswitch/vport-netdev.c index 12055af832dc08..a1df551e915bca 100644 --- a/net/openvswitch/vport-netdev.c +++ b/net/openvswitch/vport-netdev.c @@ -196,9 +196,13 @@ void ovs_netdev_tunnel_destroy(struct vport *vport) */ if (vport->dev->reg_state == NETREG_REGISTERED) rtnl_delete_link(vport->dev, 0, NULL); - rtnl_unlock(); + /* We can't put the device reference yet, since it can still be in + * use, but rtnl_unlock()->netdev_run_todo() will block until all + * the references are released, so the RCU call must be before it. + */ call_rcu(&vport->rcu, vport_netdev_free); + rtnl_unlock(); } EXPORT_SYMBOL_GPL(ovs_netdev_tunnel_destroy); -- cgit 1.3-korg
aa69918bd418openvswitch: vport: fix self-deadlock on release of tunnel ports
1 file changed · +5 −2
net/openvswitch/vport-netdev.c+5 −2 modifieddiff --git a/net/openvswitch/vport-netdev.c b/net/openvswitch/vport-netdev.c index a92ca8b37f96ab..c42642075685d7 100644 --- a/net/openvswitch/vport-netdev.c +++ b/net/openvswitch/vport-netdev.c @@ -208,9 +208,13 @@ void ovs_netdev_tunnel_destroy(struct vport *vport) */ if (vport->dev->reg_state == NETREG_REGISTERED) rtnl_delete_link(vport->dev, 0, NULL); - rtnl_unlock(); + /* We can't put the device reference yet, since it can still be in + * use, but rtnl_unlock()->netdev_run_todo() will block until all + * the references are released, so the RCU call must be before it. + */ call_rcu(&vport->rcu, vport_netdev_free); + rtnl_unlock(); } EXPORT_SYMBOL_GPL(ovs_netdev_tunnel_destroy); -- cgit 1.3-korg
Vulnerability mechanics
Root cause
"Incorrect ordering of call_rcu() and rtnl_unlock() in ovs_netdev_tunnel_destroy() causes a self-deadlock because rtnl_unlock() blocks on netdev_run_todo() waiting for all references to be released, but the RCU callback that releases the reference is never scheduled."
Attack vector
An attacker with the ability to trigger deletion of an Open vSwitch tunnel port (e.g., by removing a tunnel netdev via ovs-vsctl or similar configuration commands) can cause a self-deadlock on the kernel. The deadlock occurs in the function ovs_netdev_tunnel_destroy() in net/openvswitch/vport-netdev.c [patch_id=2898137]. When the tunnel device is deleted, rtnl_unlock() internally calls netdev_run_todo(), which blocks until all netdev references are released. However, in the vulnerable code, call_rcu() (which schedules the callback that eventually releases the reference via netdev_put()) is placed after rtnl_unlock(), so it never executes — the kernel hangs indefinitely on device removal.
Affected code
The vulnerable function is ovs_netdev_tunnel_destroy() in net/openvswitch/vport-netdev.c [patch_id=2898137]. In the original code, rtnl_unlock() was called before call_rcu(), causing the deadlock.
What the fix does
The patch [patch_id=2898137] swaps the order of call_rcu() and rtnl_unlock() in ovs_netdev_tunnel_destroy(). By moving call_rcu(&vport->rcu, vport_netdev_free) before rtnl_unlock(), the RCU callback is scheduled before rtnl_unlock() calls netdev_run_todo(). When netdev_run_todo() invokes synchronize_net() (which waits for an RCU grace period), the already-scheduled RCU callback runs, releases the netdev reference via netdev_put(), and unblocks the teardown. This eliminates the self-deadlock while preserving the requirement that netdev_put() happens after an RCU grace period.
Preconditions
- configThe system must have Open vSwitch configured with tunnel ports (e.g., GRE, VXLAN, Geneve).
- inputAn attacker must be able to trigger deletion of an OVS tunnel vport (e.g., via ovs-vsctl del-port or netlink commands).
Generated on May 28, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
5- git.kernel.org/stable/c/366c482965c673565ecb8bcfb15d5548f13a6a10nvd
- git.kernel.org/stable/c/3df75fff46b1517eb479d8e6b8e3500763715dd0nvd
- git.kernel.org/stable/c/6522d59fb7de55ce0f0f285d962243ddffebb01fnvd
- git.kernel.org/stable/c/aa69918bd418e700309fdd08509dba324fb24296nvd
- git.kernel.org/stable/c/c741433f6c8dcdecd1d9549d89053761fd1ea413nvd
News mentions
0No linked articles in our index yet.