VYPR
Unrated severityNVD Advisory· Published May 27, 2026· Updated May 27, 2026

CVE-2026-46015

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

1

Patches

10
ab5fdcd53564

tcp: call sk_data_ready() after listener migration

https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.gitZhenzhong WuApr 22, 2026Fixed in 6.6.140via kernel-cna
2 files changed · +6 2
  • net/ipv4/inet_connection_sock.c+3 1 modified
    diff --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 modified
    diff --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
    
    
    
83bb57635d7c

tcp: call sk_data_ready() after listener migration

https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.gitZhenzhong WuApr 22, 2026Fixed in 6.18.27via kernel-cna
2 files changed · +6 2
  • net/ipv4/inet_connection_sock.c+3 1 modified
    diff --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 modified
    diff --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
    
    
    
3864c6ba1e04

tcp: call sk_data_ready() after listener migration

https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.gitZhenzhong WuApr 22, 2026Fixed in 7.1-rc1via kernel-cna
2 files changed · +6 2
  • net/ipv4/inet_connection_sock.c+3 1 modified
    diff --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 modified
    diff --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
    
    
    
12625b4da84c

tcp: call sk_data_ready() after listener migration

https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.gitZhenzhong WuApr 22, 2026Fixed in 7.0.4via kernel-cna
2 files changed · +6 2
  • net/ipv4/inet_connection_sock.c+3 1 modified
    diff --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 modified
    diff --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
    
    
    
bebd058ef40c

tcp: call sk_data_ready() after listener migration

https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.gitZhenzhong WuApr 22, 2026Fixed in 6.12.86via kernel-cna
2 files changed · +6 2
  • net/ipv4/inet_connection_sock.c+3 1 modified
    diff --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 modified
    diff --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
    
    
    
83bb57635d7c

tcp: call sk_data_ready() after listener migration

2 files changed · +6 2
  • net/ipv4/inet_connection_sock.c+3 1 modified
    diff --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 modified
    diff --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
    
    
    
3864c6ba1e04

tcp: call sk_data_ready() after listener migration

1 file changed · +3 1
  • net/ipv4/inet_connection_sock.c+3 1 modified
    diff --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
    
    
    
ab5fdcd53564

tcp: call sk_data_ready() after listener migration

2 files changed · +6 2
  • net/ipv4/inet_connection_sock.c+3 1 modified
    diff --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 modified
    diff --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
    
    
    
bebd058ef40c

tcp: call sk_data_ready() after listener migration

2 files changed · +6 2
  • net/ipv4/inet_connection_sock.c+3 1 modified
    diff --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 modified
    diff --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
    
    
    
12625b4da84c

tcp: call sk_data_ready() after listener migration

1 file changed · +3 1
  • net/ipv4/inet_connection_sock.c+3 1 modified
    diff --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

News mentions

0

No linked articles in our index yet.