CVE-2026-46015
Description
In the Linux kernel, the following vulnerability has been resolved:
tcp: call sk_data_ready() after listener migration
When inet_csk_listen_stop() migrates an established child socket from a closing listener to another socket in the same SO_REUSEPORT group, the target listener gets a new accept-queue entry via inet_csk_reqsk_queue_add(), but that path never notifies the target listener's waiters. A nonblocking accept() still works because it checks the queue directly, but poll()/epoll_wait() waiters and blocking accept() callers can also remain asleep indefinitely.
Call READ_ONCE(nsk->sk_data_ready)(nsk) after a successful migration in inet_csk_listen_stop().
However, after inet_csk_reqsk_queue_add() succeeds, the ref acquired in reuseport_migrate_sock() is effectively transferred to nreq->rsk_listener. Another CPU can then dequeue nreq via accept() or listener shutdown, hit reqsk_put(), and drop that listener ref. Since listeners are SOCK_RCU_FREE, wrap the post-queue_add() dereferences of nsk in rcu_read_lock()/rcu_read_unlock(), which also covers the existing sock_net(nsk) access in that path.
The reqsk_timer_handler() path does not need the same changes for two reasons: half-open requests become readable only after the final ACK, where tcp_child_process() already wakes the listener; and once nreq is visible via inet_ehash_insert(), the success path no longer touches nsk directly.
AI Insight
LLM-synthesized narrative grounded in this CVE's description and references.
Missing sk_data_ready() after TCP listener migration in Linux kernel can cause poll/blocking accept to hang indefinitely.
Vulnerability
In the Linux kernel, a bug exists in inet_csk_listen_stop() when migrating an established child socket from a closing listener to another socket in the same SO_REUSEPORT group. The target listener receives a new accept-queue entry via inet_csk_reqsk_queue_add(), but that path never calls sk_data_ready() to notify the target listener's waiters. This affects kernel versions prior to the fix commit [1].
Exploitation
An attacker must be able to trigger a listener migration scenario, which requires specific network conditions and SO_REUSEPORT configuration. The attacker can cause a denial of service by making poll()/epoll_wait() waiters and blocking accept() callers remain asleep indefinitely, as they are never woken up after the migration.
Impact
Successful exploitation results in a denial of service: legitimate clients cannot accept new connections on the target listener because the waiters are not notified. Nonblocking accept() still works as it checks the queue directly, but poll-based event loops and blocking accept() calls hang.
Mitigation
The fix is included in Linux kernel commit 12625b4da84c (stable) [1]. Users should update to a kernel containing this commit. No workaround is documented; updating the kernel is the recommended mitigation.
AI Insight generated on May 27, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.
Affected products
1Patches
10ab5fdcd53564tcp: call sk_data_ready() after listener migration
2 files changed · +6 −2
net/ipv4/inet_connection_sock.c+3 −1 modifieddiff --git a/net/ipv4/inet_connection_sock.c b/net/ipv4/inet_connection_sock.c index 7ac315b93bc605..a6f9192b4e53ce 100644 --- a/net/ipv4/inet_connection_sock.c +++ b/net/ipv4/inet_connection_sock.c @@ -1429,16 +1429,19 @@ void inet_csk_listen_stop(struct sock *sk) if (nreq) { refcount_set(&nreq->rsk_refcnt, 1); + rcu_read_lock(); if (inet_csk_reqsk_queue_add(nsk, nreq, child)) { __NET_INC_STATS(sock_net(nsk), LINUX_MIB_TCPMIGRATEREQSUCCESS); reqsk_migrate_reset(req); + READ_ONCE(nsk->sk_data_ready)(nsk); } else { __NET_INC_STATS(sock_net(nsk), LINUX_MIB_TCPMIGRATEREQFAILURE); reqsk_migrate_reset(nreq); __reqsk_free(nreq); } + rcu_read_unlock(); /* inet_csk_reqsk_queue_add() has already * called inet_child_forget() on failure case. -- cgit 1.3-korg
net/ipv4/inet_connection_sock.c+3 −1 modifieddiff --git a/net/ipv4/inet_connection_sock.c b/net/ipv4/inet_connection_sock.c index 7ac315b93bc605..a6f9192b4e53ce 100644 --- a/net/ipv4/inet_connection_sock.c +++ b/net/ipv4/inet_connection_sock.c @@ -1429,16 +1429,19 @@ void inet_csk_listen_stop(struct sock *sk) if (nreq) { refcount_set(&nreq->rsk_refcnt, 1); + rcu_read_lock(); if (inet_csk_reqsk_queue_add(nsk, nreq, child)) { __NET_INC_STATS(sock_net(nsk), LINUX_MIB_TCPMIGRATEREQSUCCESS); reqsk_migrate_reset(req); + READ_ONCE(nsk->sk_data_ready)(nsk); } else { __NET_INC_STATS(sock_net(nsk), LINUX_MIB_TCPMIGRATEREQFAILURE); reqsk_migrate_reset(nreq); __reqsk_free(nreq); } + rcu_read_unlock(); /* inet_csk_reqsk_queue_add() has already * called inet_child_forget() on failure case. -- cgit 1.3-korg
83bb57635d7ctcp: call sk_data_ready() after listener migration
2 files changed · +6 −2
net/ipv4/inet_connection_sock.c+3 −1 modifieddiff --git a/net/ipv4/inet_connection_sock.c b/net/ipv4/inet_connection_sock.c index 7a2f116106e96a..ed773cd488769d 100644 --- a/net/ipv4/inet_connection_sock.c +++ b/net/ipv4/inet_connection_sock.c @@ -1486,16 +1486,19 @@ void inet_csk_listen_stop(struct sock *sk) if (nreq) { refcount_set(&nreq->rsk_refcnt, 1); + rcu_read_lock(); if (inet_csk_reqsk_queue_add(nsk, nreq, child)) { __NET_INC_STATS(sock_net(nsk), LINUX_MIB_TCPMIGRATEREQSUCCESS); reqsk_migrate_reset(req); + READ_ONCE(nsk->sk_data_ready)(nsk); } else { __NET_INC_STATS(sock_net(nsk), LINUX_MIB_TCPMIGRATEREQFAILURE); reqsk_migrate_reset(nreq); __reqsk_free(nreq); } + rcu_read_unlock(); /* inet_csk_reqsk_queue_add() has already * called inet_child_forget() on failure case. -- cgit 1.3-korg
net/ipv4/inet_connection_sock.c+3 −1 modifieddiff --git a/net/ipv4/inet_connection_sock.c b/net/ipv4/inet_connection_sock.c index 7a2f116106e96a..ed773cd488769d 100644 --- a/net/ipv4/inet_connection_sock.c +++ b/net/ipv4/inet_connection_sock.c @@ -1486,16 +1486,19 @@ void inet_csk_listen_stop(struct sock *sk) if (nreq) { refcount_set(&nreq->rsk_refcnt, 1); + rcu_read_lock(); if (inet_csk_reqsk_queue_add(nsk, nreq, child)) { __NET_INC_STATS(sock_net(nsk), LINUX_MIB_TCPMIGRATEREQSUCCESS); reqsk_migrate_reset(req); + READ_ONCE(nsk->sk_data_ready)(nsk); } else { __NET_INC_STATS(sock_net(nsk), LINUX_MIB_TCPMIGRATEREQFAILURE); reqsk_migrate_reset(nreq); __reqsk_free(nreq); } + rcu_read_unlock(); /* inet_csk_reqsk_queue_add() has already * called inet_child_forget() on failure case. -- cgit 1.3-korg
3864c6ba1e04tcp: call sk_data_ready() after listener migration
2 files changed · +6 −2
net/ipv4/inet_connection_sock.c+3 −1 modifieddiff --git a/net/ipv4/inet_connection_sock.c b/net/ipv4/inet_connection_sock.c index 4ac3ae1bc1afc3..928654c34156b6 100644 --- a/net/ipv4/inet_connection_sock.c +++ b/net/ipv4/inet_connection_sock.c @@ -1479,16 +1479,19 @@ void inet_csk_listen_stop(struct sock *sk) if (nreq) { refcount_set(&nreq->rsk_refcnt, 1); + rcu_read_lock(); if (inet_csk_reqsk_queue_add(nsk, nreq, child)) { __NET_INC_STATS(sock_net(nsk), LINUX_MIB_TCPMIGRATEREQSUCCESS); reqsk_migrate_reset(req); + READ_ONCE(nsk->sk_data_ready)(nsk); } else { __NET_INC_STATS(sock_net(nsk), LINUX_MIB_TCPMIGRATEREQFAILURE); reqsk_migrate_reset(nreq); __reqsk_free(nreq); } + rcu_read_unlock(); /* inet_csk_reqsk_queue_add() has already * called inet_child_forget() on failure case. -- cgit 1.3-korg
net/ipv4/inet_connection_sock.c+3 −1 modifieddiff --git a/net/ipv4/inet_connection_sock.c b/net/ipv4/inet_connection_sock.c index 4ac3ae1bc1afc3..928654c34156b6 100644 --- a/net/ipv4/inet_connection_sock.c +++ b/net/ipv4/inet_connection_sock.c @@ -1479,16 +1479,19 @@ void inet_csk_listen_stop(struct sock *sk) if (nreq) { refcount_set(&nreq->rsk_refcnt, 1); + rcu_read_lock(); if (inet_csk_reqsk_queue_add(nsk, nreq, child)) { __NET_INC_STATS(sock_net(nsk), LINUX_MIB_TCPMIGRATEREQSUCCESS); reqsk_migrate_reset(req); + READ_ONCE(nsk->sk_data_ready)(nsk); } else { __NET_INC_STATS(sock_net(nsk), LINUX_MIB_TCPMIGRATEREQFAILURE); reqsk_migrate_reset(nreq); __reqsk_free(nreq); } + rcu_read_unlock(); /* inet_csk_reqsk_queue_add() has already * called inet_child_forget() on failure case. -- cgit 1.3-korg
12625b4da84ctcp: call sk_data_ready() after listener migration
2 files changed · +6 −2
net/ipv4/inet_connection_sock.c+3 −1 modifieddiff --git a/net/ipv4/inet_connection_sock.c b/net/ipv4/inet_connection_sock.c index e961936b6be76b..bc987a59a0952c 100644 --- a/net/ipv4/inet_connection_sock.c +++ b/net/ipv4/inet_connection_sock.c @@ -1482,16 +1482,19 @@ void inet_csk_listen_stop(struct sock *sk) if (nreq) { refcount_set(&nreq->rsk_refcnt, 1); + rcu_read_lock(); if (inet_csk_reqsk_queue_add(nsk, nreq, child)) { __NET_INC_STATS(sock_net(nsk), LINUX_MIB_TCPMIGRATEREQSUCCESS); reqsk_migrate_reset(req); + READ_ONCE(nsk->sk_data_ready)(nsk); } else { __NET_INC_STATS(sock_net(nsk), LINUX_MIB_TCPMIGRATEREQFAILURE); reqsk_migrate_reset(nreq); __reqsk_free(nreq); } + rcu_read_unlock(); /* inet_csk_reqsk_queue_add() has already * called inet_child_forget() on failure case. -- cgit 1.3-korg
net/ipv4/inet_connection_sock.c+3 −1 modifieddiff --git a/net/ipv4/inet_connection_sock.c b/net/ipv4/inet_connection_sock.c index e961936b6be76b..bc987a59a0952c 100644 --- a/net/ipv4/inet_connection_sock.c +++ b/net/ipv4/inet_connection_sock.c @@ -1482,16 +1482,19 @@ void inet_csk_listen_stop(struct sock *sk) if (nreq) { refcount_set(&nreq->rsk_refcnt, 1); + rcu_read_lock(); if (inet_csk_reqsk_queue_add(nsk, nreq, child)) { __NET_INC_STATS(sock_net(nsk), LINUX_MIB_TCPMIGRATEREQSUCCESS); reqsk_migrate_reset(req); + READ_ONCE(nsk->sk_data_ready)(nsk); } else { __NET_INC_STATS(sock_net(nsk), LINUX_MIB_TCPMIGRATEREQFAILURE); reqsk_migrate_reset(nreq); __reqsk_free(nreq); } + rcu_read_unlock(); /* inet_csk_reqsk_queue_add() has already * called inet_child_forget() on failure case. -- cgit 1.3-korg
bebd058ef40ctcp: call sk_data_ready() after listener migration
2 files changed · +6 −2
net/ipv4/inet_connection_sock.c+3 −1 modifieddiff --git a/net/ipv4/inet_connection_sock.c b/net/ipv4/inet_connection_sock.c index 2691d69f33502d..bf4e5f49030b7d 100644 --- a/net/ipv4/inet_connection_sock.c +++ b/net/ipv4/inet_connection_sock.c @@ -1486,16 +1486,19 @@ void inet_csk_listen_stop(struct sock *sk) if (nreq) { refcount_set(&nreq->rsk_refcnt, 1); + rcu_read_lock(); if (inet_csk_reqsk_queue_add(nsk, nreq, child)) { __NET_INC_STATS(sock_net(nsk), LINUX_MIB_TCPMIGRATEREQSUCCESS); reqsk_migrate_reset(req); + READ_ONCE(nsk->sk_data_ready)(nsk); } else { __NET_INC_STATS(sock_net(nsk), LINUX_MIB_TCPMIGRATEREQFAILURE); reqsk_migrate_reset(nreq); __reqsk_free(nreq); } + rcu_read_unlock(); /* inet_csk_reqsk_queue_add() has already * called inet_child_forget() on failure case. -- cgit 1.3-korg
net/ipv4/inet_connection_sock.c+3 −1 modifieddiff --git a/net/ipv4/inet_connection_sock.c b/net/ipv4/inet_connection_sock.c index 2691d69f33502d..bf4e5f49030b7d 100644 --- a/net/ipv4/inet_connection_sock.c +++ b/net/ipv4/inet_connection_sock.c @@ -1486,16 +1486,19 @@ void inet_csk_listen_stop(struct sock *sk) if (nreq) { refcount_set(&nreq->rsk_refcnt, 1); + rcu_read_lock(); if (inet_csk_reqsk_queue_add(nsk, nreq, child)) { __NET_INC_STATS(sock_net(nsk), LINUX_MIB_TCPMIGRATEREQSUCCESS); reqsk_migrate_reset(req); + READ_ONCE(nsk->sk_data_ready)(nsk); } else { __NET_INC_STATS(sock_net(nsk), LINUX_MIB_TCPMIGRATEREQFAILURE); reqsk_migrate_reset(nreq); __reqsk_free(nreq); } + rcu_read_unlock(); /* inet_csk_reqsk_queue_add() has already * called inet_child_forget() on failure case. -- cgit 1.3-korg
83bb57635d7ctcp: call sk_data_ready() after listener migration
2 files changed · +6 −2
net/ipv4/inet_connection_sock.c+3 −1 modifieddiff --git a/net/ipv4/inet_connection_sock.c b/net/ipv4/inet_connection_sock.c index 7a2f116106e96a..ed773cd488769d 100644 --- a/net/ipv4/inet_connection_sock.c +++ b/net/ipv4/inet_connection_sock.c @@ -1486,16 +1486,19 @@ void inet_csk_listen_stop(struct sock *sk) if (nreq) { refcount_set(&nreq->rsk_refcnt, 1); + rcu_read_lock(); if (inet_csk_reqsk_queue_add(nsk, nreq, child)) { __NET_INC_STATS(sock_net(nsk), LINUX_MIB_TCPMIGRATEREQSUCCESS); reqsk_migrate_reset(req); + READ_ONCE(nsk->sk_data_ready)(nsk); } else { __NET_INC_STATS(sock_net(nsk), LINUX_MIB_TCPMIGRATEREQFAILURE); reqsk_migrate_reset(nreq); __reqsk_free(nreq); } + rcu_read_unlock(); /* inet_csk_reqsk_queue_add() has already * called inet_child_forget() on failure case. -- cgit 1.3-korg
net/ipv4/inet_connection_sock.c+3 −1 modifieddiff --git a/net/ipv4/inet_connection_sock.c b/net/ipv4/inet_connection_sock.c index 7a2f116106e96a..ed773cd488769d 100644 --- a/net/ipv4/inet_connection_sock.c +++ b/net/ipv4/inet_connection_sock.c @@ -1486,16 +1486,19 @@ void inet_csk_listen_stop(struct sock *sk) if (nreq) { refcount_set(&nreq->rsk_refcnt, 1); + rcu_read_lock(); if (inet_csk_reqsk_queue_add(nsk, nreq, child)) { __NET_INC_STATS(sock_net(nsk), LINUX_MIB_TCPMIGRATEREQSUCCESS); reqsk_migrate_reset(req); + READ_ONCE(nsk->sk_data_ready)(nsk); } else { __NET_INC_STATS(sock_net(nsk), LINUX_MIB_TCPMIGRATEREQFAILURE); reqsk_migrate_reset(nreq); __reqsk_free(nreq); } + rcu_read_unlock(); /* inet_csk_reqsk_queue_add() has already * called inet_child_forget() on failure case. -- cgit 1.3-korg
3864c6ba1e04tcp: call sk_data_ready() after listener migration
1 file changed · +3 −1
net/ipv4/inet_connection_sock.c+3 −1 modifieddiff --git a/net/ipv4/inet_connection_sock.c b/net/ipv4/inet_connection_sock.c index 4ac3ae1bc1afc3..928654c34156b6 100644 --- a/net/ipv4/inet_connection_sock.c +++ b/net/ipv4/inet_connection_sock.c @@ -1479,16 +1479,19 @@ void inet_csk_listen_stop(struct sock *sk) if (nreq) { refcount_set(&nreq->rsk_refcnt, 1); + rcu_read_lock(); if (inet_csk_reqsk_queue_add(nsk, nreq, child)) { __NET_INC_STATS(sock_net(nsk), LINUX_MIB_TCPMIGRATEREQSUCCESS); reqsk_migrate_reset(req); + READ_ONCE(nsk->sk_data_ready)(nsk); } else { __NET_INC_STATS(sock_net(nsk), LINUX_MIB_TCPMIGRATEREQFAILURE); reqsk_migrate_reset(nreq); __reqsk_free(nreq); } + rcu_read_unlock(); /* inet_csk_reqsk_queue_add() has already * called inet_child_forget() on failure case. -- cgit 1.3-korg
ab5fdcd53564tcp: call sk_data_ready() after listener migration
2 files changed · +6 −2
net/ipv4/inet_connection_sock.c+3 −1 modifieddiff --git a/net/ipv4/inet_connection_sock.c b/net/ipv4/inet_connection_sock.c index 7ac315b93bc605..a6f9192b4e53ce 100644 --- a/net/ipv4/inet_connection_sock.c +++ b/net/ipv4/inet_connection_sock.c @@ -1429,16 +1429,19 @@ void inet_csk_listen_stop(struct sock *sk) if (nreq) { refcount_set(&nreq->rsk_refcnt, 1); + rcu_read_lock(); if (inet_csk_reqsk_queue_add(nsk, nreq, child)) { __NET_INC_STATS(sock_net(nsk), LINUX_MIB_TCPMIGRATEREQSUCCESS); reqsk_migrate_reset(req); + READ_ONCE(nsk->sk_data_ready)(nsk); } else { __NET_INC_STATS(sock_net(nsk), LINUX_MIB_TCPMIGRATEREQFAILURE); reqsk_migrate_reset(nreq); __reqsk_free(nreq); } + rcu_read_unlock(); /* inet_csk_reqsk_queue_add() has already * called inet_child_forget() on failure case. -- cgit 1.3-korg
net/ipv4/inet_connection_sock.c+3 −1 modifieddiff --git a/net/ipv4/inet_connection_sock.c b/net/ipv4/inet_connection_sock.c index 7ac315b93bc605..a6f9192b4e53ce 100644 --- a/net/ipv4/inet_connection_sock.c +++ b/net/ipv4/inet_connection_sock.c @@ -1429,16 +1429,19 @@ void inet_csk_listen_stop(struct sock *sk) if (nreq) { refcount_set(&nreq->rsk_refcnt, 1); + rcu_read_lock(); if (inet_csk_reqsk_queue_add(nsk, nreq, child)) { __NET_INC_STATS(sock_net(nsk), LINUX_MIB_TCPMIGRATEREQSUCCESS); reqsk_migrate_reset(req); + READ_ONCE(nsk->sk_data_ready)(nsk); } else { __NET_INC_STATS(sock_net(nsk), LINUX_MIB_TCPMIGRATEREQFAILURE); reqsk_migrate_reset(nreq); __reqsk_free(nreq); } + rcu_read_unlock(); /* inet_csk_reqsk_queue_add() has already * called inet_child_forget() on failure case. -- cgit 1.3-korg
bebd058ef40ctcp: call sk_data_ready() after listener migration
2 files changed · +6 −2
net/ipv4/inet_connection_sock.c+3 −1 modifieddiff --git a/net/ipv4/inet_connection_sock.c b/net/ipv4/inet_connection_sock.c index 2691d69f33502d..bf4e5f49030b7d 100644 --- a/net/ipv4/inet_connection_sock.c +++ b/net/ipv4/inet_connection_sock.c @@ -1486,16 +1486,19 @@ void inet_csk_listen_stop(struct sock *sk) if (nreq) { refcount_set(&nreq->rsk_refcnt, 1); + rcu_read_lock(); if (inet_csk_reqsk_queue_add(nsk, nreq, child)) { __NET_INC_STATS(sock_net(nsk), LINUX_MIB_TCPMIGRATEREQSUCCESS); reqsk_migrate_reset(req); + READ_ONCE(nsk->sk_data_ready)(nsk); } else { __NET_INC_STATS(sock_net(nsk), LINUX_MIB_TCPMIGRATEREQFAILURE); reqsk_migrate_reset(nreq); __reqsk_free(nreq); } + rcu_read_unlock(); /* inet_csk_reqsk_queue_add() has already * called inet_child_forget() on failure case. -- cgit 1.3-korg
net/ipv4/inet_connection_sock.c+3 −1 modifieddiff --git a/net/ipv4/inet_connection_sock.c b/net/ipv4/inet_connection_sock.c index 2691d69f33502d..bf4e5f49030b7d 100644 --- a/net/ipv4/inet_connection_sock.c +++ b/net/ipv4/inet_connection_sock.c @@ -1486,16 +1486,19 @@ void inet_csk_listen_stop(struct sock *sk) if (nreq) { refcount_set(&nreq->rsk_refcnt, 1); + rcu_read_lock(); if (inet_csk_reqsk_queue_add(nsk, nreq, child)) { __NET_INC_STATS(sock_net(nsk), LINUX_MIB_TCPMIGRATEREQSUCCESS); reqsk_migrate_reset(req); + READ_ONCE(nsk->sk_data_ready)(nsk); } else { __NET_INC_STATS(sock_net(nsk), LINUX_MIB_TCPMIGRATEREQFAILURE); reqsk_migrate_reset(nreq); __reqsk_free(nreq); } + rcu_read_unlock(); /* inet_csk_reqsk_queue_add() has already * called inet_child_forget() on failure case. -- cgit 1.3-korg
12625b4da84ctcp: call sk_data_ready() after listener migration
1 file changed · +3 −1
net/ipv4/inet_connection_sock.c+3 −1 modifieddiff --git a/net/ipv4/inet_connection_sock.c b/net/ipv4/inet_connection_sock.c index e961936b6be76b..bc987a59a0952c 100644 --- a/net/ipv4/inet_connection_sock.c +++ b/net/ipv4/inet_connection_sock.c @@ -1482,16 +1482,19 @@ void inet_csk_listen_stop(struct sock *sk) if (nreq) { refcount_set(&nreq->rsk_refcnt, 1); + rcu_read_lock(); if (inet_csk_reqsk_queue_add(nsk, nreq, child)) { __NET_INC_STATS(sock_net(nsk), LINUX_MIB_TCPMIGRATEREQSUCCESS); reqsk_migrate_reset(req); + READ_ONCE(nsk->sk_data_ready)(nsk); } else { __NET_INC_STATS(sock_net(nsk), LINUX_MIB_TCPMIGRATEREQFAILURE); reqsk_migrate_reset(nreq); __reqsk_free(nreq); } + rcu_read_unlock(); /* inet_csk_reqsk_queue_add() has already * called inet_child_forget() on failure case. -- cgit 1.3-korg
Vulnerability mechanics
Root cause
"Missing wake-up notification (sk_data_ready) after migrating a child socket's accept-queue entry to a target listener in inet_csk_listen_stop()."
Attack vector
An attacker can cause a denial-of-service by having a closing SO_REUSEPORT listener migrate an established child socket to a target listener. After migration, the target listener's accept queue is populated but `poll()`/`epoll_wait()` waiters and blocking `accept()` callers on the target listener remain asleep indefinitely because `sk_data_ready()` is never called [patch_id=2660431]. Nonblocking `accept()` still works because it checks the queue directly, but any process relying on event notification will hang. The attacker needs to arrange for a listener in a SO_REUSEPORT group to close while it has established child sockets that get migrated to another listener in the same group.
Affected code
The vulnerability is in the function `inet_csk_listen_stop()` in `net/ipv4/inet_connection_sock.c` [patch_id=2660431]. When a closing listener migrates an established child socket to another listener in the same SO_REUSEPORT group, the target listener's accept queue is updated via `inet_csk_reqsk_queue_add()`, but no wake-up notification is sent to waiters on the target listener.
What the fix does
The patch adds a call to `READ_ONCE(nsk->sk_data_ready)(nsk)` after a successful migration in `inet_csk_listen_stop()` [patch_id=2660431]. This wakes up any waiters (poll/epoll/blocking accept) on the target listener so they know a new connection is available. The patch also wraps the post-`inet_csk_reqsk_queue_add()` code in `rcu_read_lock()`/`rcu_read_unlock()` because after the queue add succeeds, another CPU could dequeue the request and drop the listener's reference count; since listeners use `SOCK_RCU_FREE`, the RCU read lock protects the `nsk` dereferences [patch_id=2660431].
Preconditions
- configMultiple sockets must be bound to the same port via SO_REUSEPORT.
- inputA listener in the SO_REUSEPORT group must be closing while it has established child sockets.
- inputThe established child socket must be migrated to another listener in the same SO_REUSEPORT group.
- inputA process must be blocked on poll()/epoll_wait() or blocking accept() on the target listener.
Generated on May 27, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
5- git.kernel.org/stable/c/12625b4da84caf4d84a04988710a7b9bcf702b18nvd
- git.kernel.org/stable/c/3864c6ba1e041bc75342353a70fa2a2c6f909923nvd
- git.kernel.org/stable/c/83bb57635d7cbafde32f865b577ecfd969f02337nvd
- git.kernel.org/stable/c/ab5fdcd535645f6dbe6e9e21d96a08d141e88b4bnvd
- git.kernel.org/stable/c/bebd058ef40c67a81fe6d9ee8beaa4ede90e0704nvd
News mentions
0No linked articles in our index yet.