On 8/9/21 11:39 PM, Florian Westphal wrote: > Pavel Skripkin 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 * Hi, Florian! > > Yes and no. The above is fine since pcpu will return different pointers > for cpu 0 and 1. > Dumb question: why per_cpu() will return 2 different pointers for CPU 1 and CPU 0? As I understand for_each_possible_cpu() will iterate over all CPUs which could ever be enabled. So, we can hit situation when 2 concurrent processes call per_cpu() with same cpu value (*). > 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? > Anyway, I think, moving locking a bit higher is good here, let's test it. I will prepare a patch, if it will pass syzbot testing, thanks! #syz test git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git master With regards, Pavel Skripkin