Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

netfilter: nft_ct: Use __refcount_inc() for per-CPU nft_ct_pcpu_template.

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>

authored by

Sebastian Andrzej Siewior and committed by
Pablo Neira Ayuso
5cfe5612 a466fd7e

+4 -2
+4 -2
net/netfilter/nft_ct.c
··· 230 230 enum ip_conntrack_info ctinfo; 231 231 u16 value = nft_reg_load16(&regs->data[priv->sreg]); 232 232 struct nf_conn *ct; 233 + int oldcnt; 233 234 234 235 ct = nf_ct_get(skb, &ctinfo); 235 236 if (ct) /* already tracked */ ··· 251 250 252 251 ct = this_cpu_read(nft_ct_pcpu_template); 253 252 254 - if (likely(refcount_read(&ct->ct_general.use) == 1)) { 255 - refcount_inc(&ct->ct_general.use); 253 + __refcount_inc(&ct->ct_general.use, &oldcnt); 254 + if (likely(oldcnt == 1)) { 256 255 nf_ct_zone_add(ct, &zone); 257 256 } else { 257 + refcount_dec(&ct->ct_general.use); 258 258 /* previous skb got queued to userspace, allocate temporary 259 259 * one until percpu template can be reused. 260 260 */