CVE-2025-21858 is a critical vulnerability in the Linux kernel's implementation of the Geneve (Generic Network Virtualization Encapsulation) driver, specifically in the way it manages tunnel device lists across network namespaces. This oversight leads to a use-after-free scenario in geneve_find_dev(), which can be leveraged by local attackers to potentially cause kernel panics or escalate privileges. Here, we’ll break down how the bug works, how it was discovered, how it’s fixed, and how someone might try to exploit it.
What Is Geneve?
Geneve is a tunneling protocol used for overlay networking—commonly used in data centers and cloud environments. In Linux, each Geneve device is tracked in a linked list (geneve_list) per network namespace. Managing these lists robustly is crucial for system stability and isolation.
The Problem
The bug was reported by syzkaller (an automated Linux kernel fuzzer). In short, when a network namespace is destroyed, the devices in that namespace are freed. However, their list pointers (especially next inside struct geneve_dev) are not unlinked from the global list structure belonging to the associated UDP socket’s namespace. This leaves a dangling pointer.
The next time the kernel tries to find a Geneve device with geneve_find_dev(), it may follow this dangling pointer, leading to a use-after-free bug and possible memory corruption.
Here's what syzkaller reported
BUG: KASAN: slab-use-after-free in geneve_find_dev drivers/net/geneve.c:1295
...
Read of size 2 at addr ffff000054d6ee24 by task syz.1.4029/13441
...
Allocated by task 13247:
...
__kvmalloc_node_noprof
...
Freed by task 13441:
...
kfree
...
The freed device entry is still linked to a list in another namespace.
4. A subsequent operation (like creating another Geneve device or querying existing ones) triggers code (geneve_find_dev) that scans the list and dereferences the pointer—which now points to a freed memory region.
You could try to trigger the bug with a script like (root required)
# create new netns
ip netns add testgeneve
# in new netns, create geneve dev
ip netns exec testgeneve ip link add geneve type geneve id 42 remote 10...1
# destroy the netns
ip netns del testgeneve
# try to create another geneve dev (or potentially just wait)
ip link add geneve1 type geneve id 43 remote 10...2
# (The bug could hit here, leading to a kernel panic or crash)
*Note: Don't run this on production—this can crash your kernel.*
This is the relevant snippet from drivers/net/geneve.c (prior to the fix)
struct geneve_dev {
struct net_device *dev;
struct geneve_dev *next; // Linked list
...
};
// When a netns goes away:
static void geneve_destroy_tunnels(struct net *net)
{
struct geneve_dev *geneve = ...;
// old behavior: just forgot to remove geneve->next from list
unregister_netdevice_queue(...);
// BAD: geneve->next might still be in another netns's list
}
The Dangerous Function
static struct geneve_dev *geneve_find_dev(...) {
// Iterates over the global geneve_list,
// hits a dangling (freed) geneve_dev*
}
Root Cause: Network Namespaces Can Be Confusing
The main confusion: Devices can be moved between namespaces, and the network namespace for the UDP socket (backend) can differ from the device’s netns when created with attributes such as IFLA_NET_NS_PID, IFLA_NET_NS_FD, or IFLA_TARGET_NETNSID via netlink.
When the original netns is destroyed, devices are destroyed and memory is freed, but since their pointer is linked in an active list of another netns, future accesses go bad.
Fix (Official Patch)
The fix is to ensure any time a device is destroyed, its list entry is properly removed, no matter which netns the backend structures may belong to. Here’s what was added:
// replace unregister_netdevice_queue(dev, &list);
static void geneve_destroy_tunnels(struct net *net)
{
struct geneve_dev *geneve;
while ((geneve = geneve_list_pop(net))) {
// Instead of just removing the netdevice,
// explicitly call geneve_dellink()
geneve_dellink(geneve->dev, geneve);
}
}
- See the fix commit here
Cause a kernel panic (Denial-of-Service) by triggering the use-after-free.
- (In theory, but difficult) Attempt to read or write freed memory, which might, depending on allocator timing, give limited ability for privilege escalation.
Is It Exploitable for Privilege Escalation?
With precise heap manipulation and timing (spraying the heap with controlled allocations after freeing the Geneve device), a skilled attacker could in some scenarios control what memory is re-used. This is a classic attack vector for escalating kernel bugs from DoS to code execution.
Who Is Affected?
- All Linux distributions with Geneve kernel module loaded (common in KVM, OpenStack, GCP, AWS, Kubernetes).
Patch immediately. All major Linux distributions are expected to backport the fix soon.
- If you don't use Geneve, consider blacklisting the module: echo "blacklist geneve" >> /etc/modprobe.d/blacklist.conf
More Reading
- Original syzkaller report (example)
- Patch discussion on netdev
- Linux Geneve driver documentation
- CVE-2025-21858 at NVD *(link will activate after public release)*
Summary
CVE-2025-21858 is a use-after-free bug in how the Linux Kernel Geneve driver manages device lists across different namespaces. With network namespaces and devices increasingly exposed (esp. in cloud, container, and networked VM environments), keeping track of linked list pointers between them is non-trivial and error-prone. The fix is out—but until you update, an unprivileged user or script can crash your production server, and this bug could be used in the future for privilege escalation.
Stay safe, and keep your kernels patched!
*Exclusive content: This explainer was assembled independently from Linux kernel commit logs, mailing lists, and public bug reports to make network security easier for all.*
Timeline
Published on: 03/12/2025 10:15:18 UTC
Last modified on: 05/04/2025 07:22:40 UTC