CVE-2026-46319
Description
Linux kernel UAF in act_ct allows privilege escalation due to a race condition in RCU lock handling.
AI Insight
LLM-synthesized narrative grounded in this CVE's description and references.
Linux kernel UAF in act_ct allows privilege escalation due to a race condition in RCU lock handling.
Vulnerability
A use-after-free vulnerability exists in the Linux kernel's net/sched: act_ct component. Specifically, in the tcf_ct_flow_table_get() function, rhashtable_lookup_fast() performs an RCU read lock and unlock before returning a flow table entry (ct_ft). This creates a race window where tcf_ct_flow_table_cleanup_work() can free the ct_ft object before its reference count is incremented, leading to a use-after-free.
Exploitation
An attacker needs to trigger a race condition between the rhashtable_lookup_fast() call and the refcount_inc_not_zero() check within tcf_ct_flow_table_get(). If the ct_ft object is freed by tcf_ct_flow_table_cleanup_work() after the RCU read lock is released but before the reference count is incremented, the subsequent use of the freed ct_ft object will occur.
Impact
Successful exploitation of this vulnerability can lead to a use-after-free condition, which can be leveraged by an attacker to achieve privilege escalation on the affected system.
Mitigation
This vulnerability has been resolved in the Linux kernel. The fix ensures that the RCU read lock is held until after the reference count is successfully incremented on the flow table entry. The specific fixed version and release date are available in the provided reference [1].
AI Insight generated on Jun 9, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.
Affected products
2Patches
163e20e1b3058enet/sched: act_ct: Only release RCU read lock after ct_ft
1 file changed · +6 −3
net/sched/act_ct.c+6 −3 modifieddiff --git a/net/sched/act_ct.c b/net/sched/act_ct.c index 7d5e50c921a07..6158e13c98d35 100644 --- a/net/sched/act_ct.c +++ b/net/sched/act_ct.c @@ -328,9 +328,13 @@ static int tcf_ct_flow_table_get(struct net *net, struct tcf_ct_params *params) int err = -ENOMEM; mutex_lock(&zones_mutex); - ct_ft = rhashtable_lookup_fast(&zones_ht, &key, zones_params); - if (ct_ft && refcount_inc_not_zero(&ct_ft->ref)) + rcu_read_lock(); + ct_ft = rhashtable_lookup(&zones_ht, &key, zones_params); + if (ct_ft && refcount_inc_not_zero(&ct_ft->ref)) { + rcu_read_unlock(); goto out_unlock; + } + rcu_read_unlock(); ct_ft = kzalloc_obj(*ct_ft); if (!ct_ft) -- cgit 1.3-korg
f462dca0c841net/sched: act_ct: Only release RCU read lock after ct_ft
1 file changed · +6 −3
net/sched/act_ct.c+6 −3 modifieddiff --git a/net/sched/act_ct.c b/net/sched/act_ct.c index 7d5e50c921a07..6158e13c98d35 100644 --- a/net/sched/act_ct.c +++ b/net/sched/act_ct.c @@ -328,9 +328,13 @@ static int tcf_ct_flow_table_get(struct net *net, struct tcf_ct_params *params) int err = -ENOMEM; mutex_lock(&zones_mutex); - ct_ft = rhashtable_lookup_fast(&zones_ht, &key, zones_params); - if (ct_ft && refcount_inc_not_zero(&ct_ft->ref)) + rcu_read_lock(); + ct_ft = rhashtable_lookup(&zones_ht, &key, zones_params); + if (ct_ft && refcount_inc_not_zero(&ct_ft->ref)) { + rcu_read_unlock(); goto out_unlock; + } + rcu_read_unlock(); ct_ft = kzalloc_obj(*ct_ft); if (!ct_ft) -- cgit 1.3-korg
17dfb67cb399net/sched: act_ct: Only release RCU read lock after ct_ft
1 file changed · +6 −3
net/sched/act_ct.c+6 −3 modifieddiff --git a/net/sched/act_ct.c b/net/sched/act_ct.c index 945b64be4c1f1..c82755749211c 100644 --- a/net/sched/act_ct.c +++ b/net/sched/act_ct.c @@ -326,9 +326,13 @@ static int tcf_ct_flow_table_get(struct net *net, struct tcf_ct_params *params) int err = -ENOMEM; mutex_lock(&zones_mutex); - ct_ft = rhashtable_lookup_fast(&zones_ht, &key, zones_params); - if (ct_ft && refcount_inc_not_zero(&ct_ft->ref)) + rcu_read_lock(); + ct_ft = rhashtable_lookup(&zones_ht, &key, zones_params); + if (ct_ft && refcount_inc_not_zero(&ct_ft->ref)) { + rcu_read_unlock(); goto out_unlock; + } + rcu_read_unlock(); ct_ft = kzalloc(sizeof(*ct_ft), GFP_KERNEL); if (!ct_ft) -- cgit 1.3-korg
4c727c6967a4net/sched: act_ct: Only release RCU read lock after ct_ft
1 file changed · +6 −3
net/sched/act_ct.c+6 −3 modifieddiff --git a/net/sched/act_ct.c b/net/sched/act_ct.c index b3c160ad590d2..f9cb8f7474ed3 100644 --- a/net/sched/act_ct.c +++ b/net/sched/act_ct.c @@ -326,9 +326,13 @@ static int tcf_ct_flow_table_get(struct net *net, struct tcf_ct_params *params) int err = -ENOMEM; mutex_lock(&zones_mutex); - ct_ft = rhashtable_lookup_fast(&zones_ht, &key, zones_params); - if (ct_ft && refcount_inc_not_zero(&ct_ft->ref)) + rcu_read_lock(); + ct_ft = rhashtable_lookup(&zones_ht, &key, zones_params); + if (ct_ft && refcount_inc_not_zero(&ct_ft->ref)) { + rcu_read_unlock(); goto out_unlock; + } + rcu_read_unlock(); ct_ft = kzalloc(sizeof(*ct_ft), GFP_KERNEL); if (!ct_ft) -- cgit 1.3-korg
ece578ca61e5net/sched: act_ct: Only release RCU read lock after ct_ft
1 file changed · +6 −3
net/sched/act_ct.c+6 −3 modifieddiff --git a/net/sched/act_ct.c b/net/sched/act_ct.c index d75f4b2b97daa..adb421684440a 100644 --- a/net/sched/act_ct.c +++ b/net/sched/act_ct.c @@ -289,9 +289,13 @@ static int tcf_ct_flow_table_get(struct net *net, struct tcf_ct_params *params) int err = -ENOMEM; mutex_lock(&zones_mutex); - ct_ft = rhashtable_lookup_fast(&zones_ht, &key, zones_params); - if (ct_ft && refcount_inc_not_zero(&ct_ft->ref)) + rcu_read_lock(); + ct_ft = rhashtable_lookup(&zones_ht, &key, zones_params); + if (ct_ft && refcount_inc_not_zero(&ct_ft->ref)) { + rcu_read_unlock(); goto out_unlock; + } + rcu_read_unlock(); ct_ft = kzalloc(sizeof(*ct_ft), GFP_KERNEL); if (!ct_ft) -- cgit 1.3-korg
a2e0c045c87anet/sched: act_ct: Only release RCU read lock after ct_ft
1 file changed · +6 −3
net/sched/act_ct.c+6 −3 modifieddiff --git a/net/sched/act_ct.c b/net/sched/act_ct.c index 171ebf4594793..1639cc2869ef1 100644 --- a/net/sched/act_ct.c +++ b/net/sched/act_ct.c @@ -290,9 +290,13 @@ static int tcf_ct_flow_table_get(struct net *net, struct tcf_ct_params *params) int err = -ENOMEM; mutex_lock(&zones_mutex); - ct_ft = rhashtable_lookup_fast(&zones_ht, &key, zones_params); - if (ct_ft && refcount_inc_not_zero(&ct_ft->ref)) + rcu_read_lock(); + ct_ft = rhashtable_lookup(&zones_ht, &key, zones_params); + if (ct_ft && refcount_inc_not_zero(&ct_ft->ref)) { + rcu_read_unlock(); goto out_unlock; + } + rcu_read_unlock(); ct_ft = kzalloc(sizeof(*ct_ft), GFP_KERNEL); if (!ct_ft) -- cgit 1.3-korg
67c9ecc9f257net/sched: act_ct: Only release RCU read lock after ct_ft
1 file changed · +6 −3
net/sched/act_ct.c+6 −3 modifieddiff --git a/net/sched/act_ct.c b/net/sched/act_ct.c index 75a8fba9fa57a..651701a186fec 100644 --- a/net/sched/act_ct.c +++ b/net/sched/act_ct.c @@ -324,9 +324,13 @@ static int tcf_ct_flow_table_get(struct net *net, struct tcf_ct_params *params) int err = -ENOMEM; mutex_lock(&zones_mutex); - ct_ft = rhashtable_lookup_fast(&zones_ht, &key, zones_params); - if (ct_ft && refcount_inc_not_zero(&ct_ft->ref)) + rcu_read_lock(); + ct_ft = rhashtable_lookup(&zones_ht, &key, zones_params); + if (ct_ft && refcount_inc_not_zero(&ct_ft->ref)) { + rcu_read_unlock(); goto out_unlock; + } + rcu_read_unlock(); ct_ft = kzalloc(sizeof(*ct_ft), GFP_KERNEL); if (!ct_ft) -- cgit 1.3-korg
f23424a0ddadnet/sched: act_ct: Only release RCU read lock after ct_ft
1 file changed · +6 −3
net/sched/act_ct.c+6 −3 modifieddiff --git a/net/sched/act_ct.c b/net/sched/act_ct.c index a5f4a27c8dd31..7810c9d64ff3a 100644 --- a/net/sched/act_ct.c +++ b/net/sched/act_ct.c @@ -328,9 +328,13 @@ static int tcf_ct_flow_table_get(struct net *net, struct tcf_ct_params *params) int err = -ENOMEM; mutex_lock(&zones_mutex); - ct_ft = rhashtable_lookup_fast(&zones_ht, &key, zones_params); - if (ct_ft && refcount_inc_not_zero(&ct_ft->ref)) + rcu_read_lock(); + ct_ft = rhashtable_lookup(&zones_ht, &key, zones_params); + if (ct_ft && refcount_inc_not_zero(&ct_ft->ref)) { + rcu_read_unlock(); goto out_unlock; + } + rcu_read_unlock(); ct_ft = kzalloc(sizeof(*ct_ft), GFP_KERNEL); if (!ct_ft) -- cgit 1.3-korg
a2e0c045c87anet/sched: act_ct: Only release RCU read lock after ct_ft
1 file changed · +6 −3
net/sched/act_ct.c+6 −3 modifieddiff --git a/net/sched/act_ct.c b/net/sched/act_ct.c index 171ebf4594793..1639cc2869ef1 100644 --- a/net/sched/act_ct.c +++ b/net/sched/act_ct.c @@ -290,9 +290,13 @@ static int tcf_ct_flow_table_get(struct net *net, struct tcf_ct_params *params) int err = -ENOMEM; mutex_lock(&zones_mutex); - ct_ft = rhashtable_lookup_fast(&zones_ht, &key, zones_params); - if (ct_ft && refcount_inc_not_zero(&ct_ft->ref)) + rcu_read_lock(); + ct_ft = rhashtable_lookup(&zones_ht, &key, zones_params); + if (ct_ft && refcount_inc_not_zero(&ct_ft->ref)) { + rcu_read_unlock(); goto out_unlock; + } + rcu_read_unlock(); ct_ft = kzalloc(sizeof(*ct_ft), GFP_KERNEL); if (!ct_ft) -- cgit 1.3-korg
4c727c6967a4net/sched: act_ct: Only release RCU read lock after ct_ft
1 file changed · +6 −3
net/sched/act_ct.c+6 −3 modifieddiff --git a/net/sched/act_ct.c b/net/sched/act_ct.c index b3c160ad590d2..f9cb8f7474ed3 100644 --- a/net/sched/act_ct.c +++ b/net/sched/act_ct.c @@ -326,9 +326,13 @@ static int tcf_ct_flow_table_get(struct net *net, struct tcf_ct_params *params) int err = -ENOMEM; mutex_lock(&zones_mutex); - ct_ft = rhashtable_lookup_fast(&zones_ht, &key, zones_params); - if (ct_ft && refcount_inc_not_zero(&ct_ft->ref)) + rcu_read_lock(); + ct_ft = rhashtable_lookup(&zones_ht, &key, zones_params); + if (ct_ft && refcount_inc_not_zero(&ct_ft->ref)) { + rcu_read_unlock(); goto out_unlock; + } + rcu_read_unlock(); ct_ft = kzalloc(sizeof(*ct_ft), GFP_KERNEL); if (!ct_ft) -- cgit 1.3-korg
3e20e1b3058enet/sched: act_ct: Only release RCU read lock after ct_ft
1 file changed · +6 −3
net/sched/act_ct.c+6 −3 modifieddiff --git a/net/sched/act_ct.c b/net/sched/act_ct.c index 7d5e50c921a07..6158e13c98d35 100644 --- a/net/sched/act_ct.c +++ b/net/sched/act_ct.c @@ -328,9 +328,13 @@ static int tcf_ct_flow_table_get(struct net *net, struct tcf_ct_params *params) int err = -ENOMEM; mutex_lock(&zones_mutex); - ct_ft = rhashtable_lookup_fast(&zones_ht, &key, zones_params); - if (ct_ft && refcount_inc_not_zero(&ct_ft->ref)) + rcu_read_lock(); + ct_ft = rhashtable_lookup(&zones_ht, &key, zones_params); + if (ct_ft && refcount_inc_not_zero(&ct_ft->ref)) { + rcu_read_unlock(); goto out_unlock; + } + rcu_read_unlock(); ct_ft = kzalloc_obj(*ct_ft); if (!ct_ft) -- cgit 1.3-korg
67c9ecc9f257net/sched: act_ct: Only release RCU read lock after ct_ft
1 file changed · +6 −3
net/sched/act_ct.c+6 −3 modifieddiff --git a/net/sched/act_ct.c b/net/sched/act_ct.c index 75a8fba9fa57a..651701a186fec 100644 --- a/net/sched/act_ct.c +++ b/net/sched/act_ct.c @@ -324,9 +324,13 @@ static int tcf_ct_flow_table_get(struct net *net, struct tcf_ct_params *params) int err = -ENOMEM; mutex_lock(&zones_mutex); - ct_ft = rhashtable_lookup_fast(&zones_ht, &key, zones_params); - if (ct_ft && refcount_inc_not_zero(&ct_ft->ref)) + rcu_read_lock(); + ct_ft = rhashtable_lookup(&zones_ht, &key, zones_params); + if (ct_ft && refcount_inc_not_zero(&ct_ft->ref)) { + rcu_read_unlock(); goto out_unlock; + } + rcu_read_unlock(); ct_ft = kzalloc(sizeof(*ct_ft), GFP_KERNEL); if (!ct_ft) -- cgit 1.3-korg
17dfb67cb399net/sched: act_ct: Only release RCU read lock after ct_ft
1 file changed · +6 −3
net/sched/act_ct.c+6 −3 modifieddiff --git a/net/sched/act_ct.c b/net/sched/act_ct.c index 945b64be4c1f1..c82755749211c 100644 --- a/net/sched/act_ct.c +++ b/net/sched/act_ct.c @@ -326,9 +326,13 @@ static int tcf_ct_flow_table_get(struct net *net, struct tcf_ct_params *params) int err = -ENOMEM; mutex_lock(&zones_mutex); - ct_ft = rhashtable_lookup_fast(&zones_ht, &key, zones_params); - if (ct_ft && refcount_inc_not_zero(&ct_ft->ref)) + rcu_read_lock(); + ct_ft = rhashtable_lookup(&zones_ht, &key, zones_params); + if (ct_ft && refcount_inc_not_zero(&ct_ft->ref)) { + rcu_read_unlock(); goto out_unlock; + } + rcu_read_unlock(); ct_ft = kzalloc(sizeof(*ct_ft), GFP_KERNEL); if (!ct_ft) -- cgit 1.3-korg
ece578ca61e5net/sched: act_ct: Only release RCU read lock after ct_ft
1 file changed · +6 −3
net/sched/act_ct.c+6 −3 modifieddiff --git a/net/sched/act_ct.c b/net/sched/act_ct.c index d75f4b2b97daa..adb421684440a 100644 --- a/net/sched/act_ct.c +++ b/net/sched/act_ct.c @@ -289,9 +289,13 @@ static int tcf_ct_flow_table_get(struct net *net, struct tcf_ct_params *params) int err = -ENOMEM; mutex_lock(&zones_mutex); - ct_ft = rhashtable_lookup_fast(&zones_ht, &key, zones_params); - if (ct_ft && refcount_inc_not_zero(&ct_ft->ref)) + rcu_read_lock(); + ct_ft = rhashtable_lookup(&zones_ht, &key, zones_params); + if (ct_ft && refcount_inc_not_zero(&ct_ft->ref)) { + rcu_read_unlock(); goto out_unlock; + } + rcu_read_unlock(); ct_ft = kzalloc(sizeof(*ct_ft), GFP_KERNEL); if (!ct_ft) -- cgit 1.3-korg
f23424a0ddadnet/sched: act_ct: Only release RCU read lock after ct_ft
1 file changed · +6 −3
net/sched/act_ct.c+6 −3 modifieddiff --git a/net/sched/act_ct.c b/net/sched/act_ct.c index a5f4a27c8dd31..7810c9d64ff3a 100644 --- a/net/sched/act_ct.c +++ b/net/sched/act_ct.c @@ -328,9 +328,13 @@ static int tcf_ct_flow_table_get(struct net *net, struct tcf_ct_params *params) int err = -ENOMEM; mutex_lock(&zones_mutex); - ct_ft = rhashtable_lookup_fast(&zones_ht, &key, zones_params); - if (ct_ft && refcount_inc_not_zero(&ct_ft->ref)) + rcu_read_lock(); + ct_ft = rhashtable_lookup(&zones_ht, &key, zones_params); + if (ct_ft && refcount_inc_not_zero(&ct_ft->ref)) { + rcu_read_unlock(); goto out_unlock; + } + rcu_read_unlock(); ct_ft = kzalloc(sizeof(*ct_ft), GFP_KERNEL); if (!ct_ft) -- cgit 1.3-korg
f462dca0c841net/sched: act_ct: Only release RCU read lock after ct_ft
1 file changed · +6 −3
net/sched/act_ct.c+6 −3 modifieddiff --git a/net/sched/act_ct.c b/net/sched/act_ct.c index 7d5e50c921a07..6158e13c98d35 100644 --- a/net/sched/act_ct.c +++ b/net/sched/act_ct.c @@ -328,9 +328,13 @@ static int tcf_ct_flow_table_get(struct net *net, struct tcf_ct_params *params) int err = -ENOMEM; mutex_lock(&zones_mutex); - ct_ft = rhashtable_lookup_fast(&zones_ht, &key, zones_params); - if (ct_ft && refcount_inc_not_zero(&ct_ft->ref)) + rcu_read_lock(); + ct_ft = rhashtable_lookup(&zones_ht, &key, zones_params); + if (ct_ft && refcount_inc_not_zero(&ct_ft->ref)) { + rcu_read_unlock(); goto out_unlock; + } + rcu_read_unlock(); ct_ft = kzalloc_obj(*ct_ft); if (!ct_ft) -- cgit 1.3-korg
Vulnerability mechanics
Synthesis attempt was rejected by the grounding validator. Re-run pending.
References
8- git.kernel.org/stable/c/17dfb67cb399b660105d9a8c6100851c0d0cdc70nvd
- git.kernel.org/stable/c/3e20e1b3058e0b94638e7b931c138e840e266724nvd
- git.kernel.org/stable/c/4c727c6967a41b37efe0f26332ca9ec5b74785a3nvd
- git.kernel.org/stable/c/67c9ecc9f2575273ed1323e312881fc98ac83d6dnvd
- git.kernel.org/stable/c/a2e0c045c87aa252eb61412e67dd91f2c2b19f81nvd
- git.kernel.org/stable/c/ece578ca61e572df96cfc80456357ebfae0b4b9envd
- git.kernel.org/stable/c/f23424a0ddadb494d4bd57056a7ca703312d3a7bnvd
- git.kernel.org/stable/c/f462dca0c8415bf0058d0ffa476354c4476d0f09nvd
News mentions
1- Linux Kernel: 25 Vulnerabilities Disclosed in Single Batch on June 8-9, 2026Vypr Intelligence · Jun 9, 2026