Pavel Skripkin <paskripkin@xxxxxxxxx> wrote:
I think, there a missing lock in this function:
for_each_possible_cpu(cpu) {
ct = per_cpu(nft_ct_pcpu_template, cpu);
if (!ct) >> break;
nf_ct_put(ct);
per_cpu(nft_ct_pcpu_template, cpu) = NULL;
}
Syzbot hit a UAF in nft_ct_tmpl_put_pcpu() (*), but freed template should be
NULL.
So I suspect following scenario:
CPU0: CPU1:
= per_cpu()
= per_cpu()
nf_ct_put
per_cpu = NULL
nf_ct_put()
* UAF *
Yes and no. The above is fine since pcpu will return different pointers
for cpu 0 and 1.
The race is between two different net namespaces that race when
changing nft_ct_pcpu_template_refcnt.
This happens since
commit f102d66b335a417d4848da9441f585695a838934
netfilter: nf_tables: use dedicated mutex to guard transactions
Before this, all transactions were serialized by a global mutex,
now we only serialize transactions in the same netns.
Its probably best to add
DEFINE_MUTEX(nft_ct_pcpu_mutex) and then acquire that when we need to
inc/dec the nft_ct_pcpu_template_refcnt so we can't have two distinct
cpus hitting a zero refcount.
Would you send a patch for this?