netfilter: nft_ct: Use __refcount_inc() for per-CPU nft_ct_pcpu_template.
authorSebastian Andrzej Siewior <bigeasy@linutronix.de>
Mon, 17 Feb 2025 16:02:42 +0000 (17:02 +0100)
committerPablo Neira Ayuso <pablo@netfilter.org>
Mon, 3 Mar 2025 12:46:49 +0000 (13:46 +0100)
nft_ct_pcpu_template is a per-CPU variable and relies on disabled BH for its
locking. The refcounter is read and if its value is set to one then the
refcounter is incremented and variable is used - otherwise it is already
in use and left untouched.

Without per-CPU locking in local_bh_disable() on PREEMPT_RT the
read-then-increment operation is not atomic and therefore racy.

This can be avoided by using unconditionally __refcount_inc() which will
increment counter and return the old value as an atomic operation.
In case the returned counter is not one, the variable is in use and we
need to decrement counter. Otherwise we can use it.

Use __refcount_inc() instead of read and a conditional increment.

Fixes: edee4f1e9245 ("netfilter: nft_ct: add zone id set support")
Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
Reviewed-by: Florian Westphal <fw@strlen.de>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
net/netfilter/nft_ct.c

index 2e59aba681a138b3e1a03613a16dfeb316fbc98f..d526e69a2a2b80a51b7f6b4854a2821964d874ae 100644 (file)
@@ -230,6 +230,7 @@ static void nft_ct_set_zone_eval(const struct nft_expr *expr,
        enum ip_conntrack_info ctinfo;
        u16 value = nft_reg_load16(&regs->data[priv->sreg]);
        struct nf_conn *ct;
+       int oldcnt;
 
        ct = nf_ct_get(skb, &ctinfo);
        if (ct) /* already tracked */
@@ -250,10 +251,11 @@ static void nft_ct_set_zone_eval(const struct nft_expr *expr,
 
        ct = this_cpu_read(nft_ct_pcpu_template);
 
-       if (likely(refcount_read(&ct->ct_general.use) == 1)) {
-               refcount_inc(&ct->ct_general.use);
+       __refcount_inc(&ct->ct_general.use, &oldcnt);
+       if (likely(oldcnt == 1)) {
                nf_ct_zone_add(ct, &zone);
        } else {
+               refcount_dec(&ct->ct_general.use);
                /* previous skb got queued to userspace, allocate temporary
                 * one until percpu template can be reused.
                 */