CVE-2026-10634
Description
Use-after-free in Zephyr native TCP stack's net_tcp_foreach() due to dropping tcp_lock during callback, allowing crash or potential info disclosure.
AI Insight
LLM-synthesized narrative grounded in this CVE's description and references.
Use-after-free in Zephyr native TCP stack's net_tcp_foreach() due to dropping tcp_lock during callback, allowing crash or potential info disclosure.
Vulnerability
A use-after-free vulnerability exists in the native TCP stack of Zephyr RTOS in the function net_tcp_foreach() in subsys/net/ip/tcp.c. This function iterates the global connection list using SYS_SLIST_FOR_EACH_CONTAINER_SAFE, which caches a pointer to the next list node. Prior to the fix, net_tcp_foreach() released the tcp_lock mutex while invoking the per-connection callback and re-acquired it after the callback. This created a race window where a concurrent tcp_conn_release(), running on the dedicated TCP work-queue thread when a connection's reference count drops to zero (e.g., from a remote peer closing or resetting the connection), can remove and k_mem_slab_free() the cached next connection. When the iterator advances, it dereferences the freed slab memory. The defect was introduced with the modern (TCP2) stack in 2020 and affects releases up to and including v4.4.0 [1].
Exploitation
An attacker can trigger the race condition by establishing a TCP connection to a vulnerable Zephyr device and then sending a TCP reset (RST) or initiating a graceful close. The closure causes the reference count of the connection to drop to zero, scheduling tcp_conn_release() on the TCP work-queue thread. Simultaneously, if the system administrator or automated process runs the 'net conn' network shell command (which calls net_tcp_foreach()) or triggers an interface down event (which calls net_tcp_close_all_for_iface()), the race window opens. The attacker must time the connection closure to occur while net_tcp_foreach() is in the middle of iteration, with the lock dropped. No authentication is required; the attacker only needs network access to establish and close TCP connections to the device [1].
Impact
Successful exploitation results in a use-after-free condition that can cause a system crash (denial of service). If the freed slab memory has been reallocated to another object under attacker influence, the callback may operate on that attacker-controlled object, potentially leading to information disclosure or further faults [1]. The impact is limited to availability and potentially confidentiality, with a CVSS v3 severity of 4.8 (Medium).
Mitigation
The fix is provided in commit cd85e0e, which moves the connection/context teardown in tcp_conn_release() inside the tcp_lock critical section and keeps tcp_lock held across the callback in net_tcp_foreach(). The projected fixed version is 4.5.0 (the fix is merged on main but not yet released; this forecast should be confirmed against the actual release). No workaround is explicitly mentioned in the references. Users are advised to update to the patched version once available [1][2].
AI Insight generated on Jun 15, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.
Affected products
2(expand)+ 1 more
- (no CPE)
- (no CPE)range: <=4.4.0
Patches
4cd85e0e890abnet: tcp: fix use-after-free in net_tcp_foreach()
1 file changed · +12 −4
subsys/net/ip/tcp.c+12 −4 modified@@ -906,10 +906,9 @@ static void tcp_conn_release(struct k_work *work) k_mutex_unlock(&conn->lock); + k_mutex_lock(&tcp_lock, K_FOREVER); net_context_unref(conn->context); conn->context = NULL; - - k_mutex_lock(&tcp_lock, K_FOREVER); sys_slist_find_and_remove(&tcp_conns, &conn->next); k_mutex_unlock(&tcp_lock); @@ -4540,10 +4539,19 @@ void net_tcp_foreach(net_tcp_cb_t cb, void *user_data) k_mutex_lock(&tcp_lock, K_FOREVER); SYS_SLIST_FOR_EACH_CONTAINER_SAFE(&tcp_conns, conn, tmp, next) { + /* Keep tcp_lock held while invoking the callback. + * tcp_conn_release() removes entries from this list and + * frees both conn->context and the conn slab under + * tcp_lock, so dropping the lock here would allow a + * concurrent release to free the *next* node saved by + * the _SAFE iterator, causing a use-after-free when the + * loop advances. + * + * All current callbacks are read-only diagnostics and + * never acquire tcp_lock, so holding it is safe. + */ if (atomic_get(&conn->ref_count) > 0) { - k_mutex_unlock(&tcp_lock); cb(conn, user_data); - k_mutex_lock(&tcp_lock, K_FOREVER); } }
168295f5a16dnet: tcp: fix use-after-free in net_tcp_foreach()
1 file changed · +12 −4
subsys/net/ip/tcp.c+12 −4 modified@@ -906,10 +906,9 @@ static void tcp_conn_release(struct k_work *work) k_mutex_unlock(&conn->lock); + k_mutex_lock(&tcp_lock, K_FOREVER); net_context_unref(conn->context); conn->context = NULL; - - k_mutex_lock(&tcp_lock, K_FOREVER); sys_slist_find_and_remove(&tcp_conns, &conn->next); k_mutex_unlock(&tcp_lock); @@ -4540,10 +4539,19 @@ void net_tcp_foreach(net_tcp_cb_t cb, void *user_data) k_mutex_lock(&tcp_lock, K_FOREVER); SYS_SLIST_FOR_EACH_CONTAINER_SAFE(&tcp_conns, conn, tmp, next) { + /* Keep tcp_lock held while invoking the callback. + * tcp_conn_release() removes entries from this list and + * frees both conn->context and the conn slab under + * tcp_lock, so dropping the lock here would allow a + * concurrent release to free the *next* node saved by + * the _SAFE iterator, causing a use-after-free when the + * loop advances. + * + * All current callbacks are read-only diagnostics and + * never acquire tcp_lock, so holding it is safe. + */ if (atomic_get(&conn->ref_count) > 0) { - k_mutex_unlock(&tcp_lock); cb(conn, user_data); - k_mutex_lock(&tcp_lock, K_FOREVER); } }
21a1c71f9dfenet: tcp: fix use-after-free in net_tcp_foreach()
1 file changed · +12 −4
subsys/net/ip/tcp.c+12 −4 modified@@ -809,10 +809,9 @@ static void tcp_conn_release(struct k_work *work) k_mutex_unlock(&conn->lock); + k_mutex_lock(&tcp_lock, K_FOREVER); net_context_unref(conn->context); conn->context = NULL; - - k_mutex_lock(&tcp_lock, K_FOREVER); sys_slist_find_and_remove(&tcp_conns, &conn->next); k_mutex_unlock(&tcp_lock); @@ -4381,10 +4380,19 @@ void net_tcp_foreach(net_tcp_cb_t cb, void *user_data) k_mutex_lock(&tcp_lock, K_FOREVER); SYS_SLIST_FOR_EACH_CONTAINER_SAFE(&tcp_conns, conn, tmp, next) { + /* Keep tcp_lock held while invoking the callback. + * tcp_conn_release() removes entries from this list and + * frees both conn->context and the conn slab under + * tcp_lock, so dropping the lock here would allow a + * concurrent release to free the *next* node saved by + * the _SAFE iterator, causing a use-after-free when the + * loop advances. + * + * All current callbacks are read-only diagnostics and + * never acquire tcp_lock, so holding it is safe. + */ if (atomic_get(&conn->ref_count) > 0) { - k_mutex_unlock(&tcp_lock); cb(conn, user_data); - k_mutex_lock(&tcp_lock, K_FOREVER); } }
f1c38f6cbcf5net: tcp: fix use-after-free in net_tcp_foreach()
1 file changed · +12 −4
subsys/net/ip/tcp.c+12 −4 modified@@ -904,10 +904,9 @@ static void tcp_conn_release(struct k_work *work) k_mutex_unlock(&conn->lock); + k_mutex_lock(&tcp_lock, K_FOREVER); net_context_unref(conn->context); conn->context = NULL; - - k_mutex_lock(&tcp_lock, K_FOREVER); sys_slist_find_and_remove(&tcp_conns, &conn->next); k_mutex_unlock(&tcp_lock); @@ -4512,10 +4511,19 @@ void net_tcp_foreach(net_tcp_cb_t cb, void *user_data) k_mutex_lock(&tcp_lock, K_FOREVER); SYS_SLIST_FOR_EACH_CONTAINER_SAFE(&tcp_conns, conn, tmp, next) { + /* Keep tcp_lock held while invoking the callback. + * tcp_conn_release() removes entries from this list and + * frees both conn->context and the conn slab under + * tcp_lock, so dropping the lock here would allow a + * concurrent release to free the *next* node saved by + * the _SAFE iterator, causing a use-after-free when the + * loop advances. + * + * All current callbacks are read-only diagnostics and + * never acquire tcp_lock, so holding it is safe. + */ if (atomic_get(&conn->ref_count) > 0) { - k_mutex_unlock(&tcp_lock); cb(conn, user_data); - k_mutex_lock(&tcp_lock, K_FOREVER); } }
Vulnerability mechanics
Root cause
"A TOCTOU race condition in net_tcp_foreach() where tcp_lock is dropped across the callback, allowing a concurrent tcp_conn_release() to free the cached next list node, leading to a use-after-free."
Attack vector
An attacker on the same link (AV:A) can trigger the bug by sending ordinary TCP traffic that causes a connection's reference count to drop to zero (e.g. a remote peer closing or resetting the connection). This schedules `tcp_conn_release()` on the dedicated TCP work-queue thread. Meanwhile, if a privileged local user (PR:L) invokes the `net conn` shell command or an interface-down event calls `net_tcp_close_all_for_iface()`, both of which reach `net_tcp_foreach()`, the window between dropping and re-acquiring `tcp_lock` in the iterator allows the concurrent release to free the cached next connection, leading to a use-after-free. The CVSS score is 4.0 (Medium) per the supplied vector [ref_id=1].
Affected code
The bug is in two functions within `subsys/net/ip/tcp.c`: `net_tcp_foreach()` (around line 4540) and `tcp_conn_release()` (around line 906). `net_tcp_foreach()` dropped `tcp_lock` while invoking the per-connection callback and re-acquired it afterwards. `tcp_conn_release()` performed connection teardown (`net_context_unref`, `sys_slist_find_and_remove`, and `k_mem_slab_free`) outside the `tcp_lock` critical section. The fix addresses both locations by keeping `tcp_lock` held across the callback in `net_tcp_foreach()` and moving the teardown operations in `tcp_conn_release()` inside the `tcp_lock` critical section [ref_id=1][patch_id=6083973].
What the fix does
The patch makes two changes in `subsys/net/ip/tcp.c` [patch_id=6083973]. First, in `tcp_conn_release()`, `k_mutex_lock(&tcp_lock, K_FOREVER)` is moved before `net_context_unref(conn->context)` and `conn->context = NULL`, so that the entire teardown—including removing the entry from the list (`sys_slist_find_and_remove`) and freeing the slab—runs under `tcp_lock`. Second, in `net_tcp_foreach()`, the `k_mutex_unlock`/`k_mutex_lock` pair around the callback is removed so that `tcp_lock` remains held while `cb(conn, user_data)` executes. The commit message explains that no current callback acquires `tcp_lock`, making this safe. Together these changes close the window where a concurrent `tcp_conn_release()` could free the next node cached by the `_SAFE` iterator [ref_id=2].
Preconditions
- networkAttacker and victim must be on the same link (adjacent network per CVSS AV:A).
- inputAttacker must be able to send TCP segments to trigger a connection close or reset, causing the reference count to drop to zero.
- authAn unprivileged local user must trigger net_tcp_foreach() via the 'net conn' shell command or an interface-down event must call net_tcp_close_all_for_iface() while the release work item is queued.
Generated on Jun 15, 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.