PT-2026-47756 · Linux · Linux
Published
2026-06-09
·
Updated
2026-06-09
·
CVE-2026-46319
None
No severity ratings or metrics are available. When they are, we'll update the corresponding info on the page.
In the Linux kernel, the following vulnerability has been resolved:
net/sched: act ct: Only release RCU read lock after ct ft
When looking up a flow table in act ct in tcf ct flow table get(),
rhashtable lookup fast() internally opens and closes an RCU read critical
section before returning ct ft.
The tcf ct flow table cleanup work() can complete before refcount inc not zero()
is invoked on the returned ct ft resulting in a UAF on the already freed ct ft
object. This vulnerability can lead to privilege escalation.
Analysis from zdi-disclosures@trendmicro.com:
When initializing act ct, tcf ct init() is called, which internally triggers
tcf ct flow table get().
static int tcf ct flow table get(struct net *net, struct tcf ct params *params)
{
struct zones ht key key = { .net = net, .zone = params->zone };
struct tcf ct flow table *ct ft;
int err = -ENOMEM;
mutex lock(&zones mutex);
ct ft = rhashtable lookup fast(&zones ht, &key, zones params); // [1]
if (ct ft && refcount inc not zero(&ct ft->ref)) // [2]
goto out unlock;
...
}
static always inline void *rhashtable lookup fast(
struct rhashtable *ht, const void *key,
const struct rhashtable params params)
{
void *obj;
rcu read lock();
obj = rhashtable lookup(ht, key, params);
rcu read unlock();
return obj;
}
At [1], rhashtable lookup fast() looks up and returns the corresponding ct ft
from zones ht . The lookup is performed within an RCU read critical section
through rcu read lock() / rcu read unlock(), which prevents the object from
being freed. However, at the point of function return, rcu read unlock() has
already been called, and there is nothing preventing ct ft from being freed
before reaching refcount inc not zero(&ct ft->ref) at [2]. This interval becomes
the race window, during which ct ft can be freed.
Free Process:
tcf ct flow table put() is executed through the path tcf ct cleanup() call rcu()
tcf ct params free rcu() tcf ct params free() tcf ct flow table put().
static void tcf ct flow table put(struct tcf ct flow table *ct ft)
{
if (refcount dec and test(&ct ft->ref)) {
rhashtable remove fast(&zones ht, &ct ft->node, zones params);
INIT RCU WORK(&ct ft->rwork, tcf ct flow table cleanup work); // [3]
queue rcu work(act ct wq, &ct ft->rwork);
}
}
At [3], tcf ct flow table cleanup work() is scheduled as RCU work
static void tcf ct flow table cleanup work(struct work struct *work)
{
struct tcf ct flow table *ct ft;
struct flow block *block;
ct ft = container of(to rcu work(work), struct tcf ct flow table,
rwork);
nf flow table free(&ct ft->nf ft);
block = &ct ft->nf ft.flow block;
down write(&ct ft->nf ft.flow block lock);
WARN ON(!list empty(&block->cb list));
up write(&ct ft->nf ft.flow block lock);
kfree(ct ft); // [4]
module put(THIS MODULE);
}
tcf ct flow table cleanup work() frees ct ft at [4]. When this function executes
between [1] and [2], UAF occurs.
This race condition has a very short race window, making it generally
difficult to trigger. Therefore, to trigger the vulnerability an msleep(100) was
inserted after[1]
Found an issue in the description? Have something to add? Feel free to write us 👾
Related Identifiers
Affected Products
Linux