netfilter: ipset: list:set: Decrease refcount synchronously on deletion and replace
authorStefano Brivio <sbrivio@redhat.com>
Sat, 14 Jul 2018 19:59:43 +0000 (21:59 +0200)
committerPablo Neira Ayuso <pablo@netfilter.org>
Wed, 31 Oct 2018 23:29:36 +0000 (00:29 +0100)
commit439cd39ea136d2c026805264d58a91f36b6b64ca
treee101f316b789c094ba41b74eecd28538fb717d33
parent4269fea768a11a447d8de620ce420f2214d4685c
netfilter: ipset: list:set: Decrease refcount synchronously on deletion and replace

Commit 45040978c899 ("netfilter: ipset: Fix set:list type crash
when flush/dump set in parallel") postponed decreasing set
reference counters to the RCU callback.

An 'ipset del' command can terminate before the RCU grace period
is elapsed, and if sets are listed before then, the reference
counter shown in userspace will be wrong:

 # ipset create h hash:ip; ipset create l list:set; ipset add l
 # ipset del l h; ipset list h
 Name: h
 Type: hash:ip
 Revision: 4
 Header: family inet hashsize 1024 maxelem 65536
 Size in memory: 88
 References: 1
 Number of entries: 0
 Members:
 # sleep 1; ipset list h
 Name: h
 Type: hash:ip
 Revision: 4
 Header: family inet hashsize 1024 maxelem 65536
 Size in memory: 88
 References: 0
 Number of entries: 0
 Members:

Fix this by making the reference count update synchronous again.

As a result, when sets are listed, ip_set_name_byindex() might
now fetch a set whose reference count is already zero. Instead
of relying on the reference count to protect against concurrent
set renaming, grab ip_set_ref_lock as reader and copy the name,
while holding the same lock in ip_set_rename() as writer
instead.

Reported-by: Li Shuang <shuali@redhat.com>
Fixes: 45040978c899 ("netfilter: ipset: Fix set:list type crash when flush/dump set in parallel")
Signed-off-by: Stefano Brivio <sbrivio@redhat.com>
Signed-off-by: Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
include/linux/netfilter/ipset/ip_set.h
net/netfilter/ipset/ip_set_core.c
net/netfilter/ipset/ip_set_list_set.c