Skip to content

Commit 4cc403c

Browse files
Jozsef Kadlecsikgregkh
authored andcommitted
netfilter: ipset: annotate "pos" for concurrent readers/writers
[ Upstream commit 7f74458 ] The "pos" structure member of struct hbucket stores the first free slot in the hash bucket of a hash type of set and there are concurrent readers/writers. Annotate accesses properly. Fixes: 18f84d4 ("netfilter: ipset: Introduce RCU locking in hash:* types") Signed-off-by: Jozsef Kadlecsik <kadlec@netfilter.org> Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org> Stable-dep-of: e4b4984 ("netfilter: ipset: Don't use test_bit() in lockless RCU readers in hash types") Signed-off-by: Sasha Levin <sashal@kernel.org>
1 parent c63a06c commit 4cc403c

1 file changed

Lines changed: 38 additions & 24 deletions

File tree

net/netfilter/ipset/ip_set_hash_gen.h

Lines changed: 38 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -373,8 +373,9 @@ static void
373373
mtype_ext_cleanup(struct ip_set *set, struct hbucket *n)
374374
{
375375
int i;
376+
u8 pos = smp_load_acquire(&n->pos);
376377

377-
for (i = 0; i < n->pos; i++)
378+
for (i = 0; i < pos; i++)
378379
if (test_bit(i, n->used))
379380
ip_set_ext_destroy(set, ahash_data(n, i, set->dsize));
380381
}
@@ -477,15 +478,16 @@ mtype_gc_do(struct ip_set *set, struct htype *h, struct htable *t, u32 r)
477478
#ifdef IP_SET_HASH_WITH_NETS
478479
u8 k;
479480
#endif
480-
u8 htable_bits = t->htable_bits;
481+
u8 pos, htable_bits = t->htable_bits;
481482

482483
spin_lock_bh(&t->hregion[r].lock);
483484
for (i = ahash_bucket_start(r, htable_bits);
484485
i < ahash_bucket_end(r, htable_bits); i++) {
485486
n = __ipset_dereference(hbucket(t, i));
486487
if (!n)
487488
continue;
488-
for (j = 0, d = 0; j < n->pos; j++) {
489+
pos = smp_load_acquire(&n->pos);
490+
for (j = 0, d = 0; j < pos; j++) {
489491
if (!test_bit(j, n->used)) {
490492
d++;
491493
continue;
@@ -521,7 +523,7 @@ mtype_gc_do(struct ip_set *set, struct htype *h, struct htable *t, u32 r)
521523
/* Still try to delete expired elements. */
522524
continue;
523525
tmp->size = n->size - AHASH_INIT_SIZE;
524-
for (j = 0, d = 0; j < n->pos; j++) {
526+
for (j = 0, d = 0; j < pos; j++) {
525527
if (!test_bit(j, n->used))
526528
continue;
527529
data = ahash_data(n, j, dsize);
@@ -610,7 +612,7 @@ mtype_resize(struct ip_set *set, bool retried)
610612
{
611613
struct htype *h = set->data;
612614
struct htable *t, *orig;
613-
u8 htable_bits;
615+
u8 pos, htable_bits;
614616
size_t hsize, dsize = set->dsize;
615617
#ifdef IP_SET_HASH_WITH_NETS
616618
u8 flags;
@@ -672,7 +674,8 @@ mtype_resize(struct ip_set *set, bool retried)
672674
n = __ipset_dereference(hbucket(orig, i));
673675
if (!n)
674676
continue;
675-
for (j = 0; j < n->pos; j++) {
677+
pos = smp_load_acquire(&n->pos);
678+
for (j = 0; j < pos; j++) {
676679
if (!test_bit(j, n->used))
677680
continue;
678681
data = ahash_data(n, j, dsize);
@@ -796,9 +799,10 @@ mtype_ext_size(struct ip_set *set, u32 *elements, size_t *ext_size)
796799
{
797800
struct htype *h = set->data;
798801
const struct htable *t;
799-
u32 i, j, r;
800802
struct hbucket *n;
801803
struct mtype_elem *data;
804+
u32 i, j, r;
805+
u8 pos;
802806

803807
t = rcu_dereference_bh(h->table);
804808
for (r = 0; r < ahash_numof_locks(t->htable_bits); r++) {
@@ -807,7 +811,8 @@ mtype_ext_size(struct ip_set *set, u32 *elements, size_t *ext_size)
807811
n = rcu_dereference_bh(hbucket(t, i));
808812
if (!n)
809813
continue;
810-
for (j = 0; j < n->pos; j++) {
814+
pos = smp_load_acquire(&n->pos);
815+
for (j = 0; j < pos; j++) {
811816
if (!test_bit(j, n->used))
812817
continue;
813818
data = ahash_data(n, j, set->dsize);
@@ -831,10 +836,11 @@ mtype_add(struct ip_set *set, void *value, const struct ip_set_ext *ext,
831836
const struct mtype_elem *d = value;
832837
struct mtype_elem *data;
833838
struct hbucket *n, *old = ERR_PTR(-ENOENT);
834-
int i, j = -1, npos = 0, ret;
839+
int i, j = -1, ret;
835840
bool flag_exist = flags & IPSET_FLAG_EXIST;
836841
bool deleted = false, forceadd = false, reuse = false;
837842
u32 r, key, multi = 0, elements, maxelem;
843+
u8 npos = 0;
838844

839845
rcu_read_lock_bh();
840846
t = rcu_dereference_bh(h->table);
@@ -876,8 +882,8 @@ mtype_add(struct ip_set *set, void *value, const struct ip_set_ext *ext,
876882
ext_size(AHASH_INIT_SIZE, set->dsize);
877883
goto copy_elem;
878884
}
879-
npos = n->pos;
880-
for (i = 0; i < n->pos; i++) {
885+
npos = smp_load_acquire(&n->pos);
886+
for (i = 0; i < npos; i++) {
881887
if (!test_bit(i, n->used)) {
882888
/* Reuse first deleted entry */
883889
if (j == -1) {
@@ -921,7 +927,7 @@ mtype_add(struct ip_set *set, void *value, const struct ip_set_ext *ext,
921927
if (elements >= maxelem)
922928
goto set_full;
923929
/* Create a new slot */
924-
if (n->pos >= n->size) {
930+
if (npos >= n->size) {
925931
#ifdef IP_SET_HASH_WITH_MULTI
926932
if (h->bucketsize >= AHASH_MAX_TUNED)
927933
goto set_full;
@@ -950,8 +956,7 @@ mtype_add(struct ip_set *set, void *value, const struct ip_set_ext *ext,
950956
}
951957

952958
copy_elem:
953-
j = npos;
954-
npos = n->pos + 1;
959+
j = npos++;
955960
data = ahash_data(n, j, set->dsize);
956961
copy_data:
957962
t->hregion[r].elements++;
@@ -974,7 +979,8 @@ mtype_add(struct ip_set *set, void *value, const struct ip_set_ext *ext,
974979
if (SET_WITH_TIMEOUT(set))
975980
ip_set_timeout_set(ext_timeout(data, set), ext->timeout);
976981
smp_mb__before_atomic();
977-
n->pos = npos;
982+
/* Ensure all data writes are visible before updating position */
983+
smp_store_release(&n->pos, npos);
978984
set_bit(j, n->used);
979985
if (old != ERR_PTR(-ENOENT)) {
980986
rcu_assign_pointer(hbucket(t, key), n);
@@ -1033,6 +1039,7 @@ mtype_del(struct ip_set *set, void *value, const struct ip_set_ext *ext,
10331039
int i, j, k, r, ret = -IPSET_ERR_EXIST;
10341040
u32 key, multi = 0;
10351041
size_t dsize = set->dsize;
1042+
u8 pos;
10361043

10371044
/* Userspace add and resize is excluded by the mutex.
10381045
* Kernespace add does not trigger resize.
@@ -1048,7 +1055,8 @@ mtype_del(struct ip_set *set, void *value, const struct ip_set_ext *ext,
10481055
n = rcu_dereference_bh(hbucket(t, key));
10491056
if (!n)
10501057
goto out;
1051-
for (i = 0, k = 0; i < n->pos; i++) {
1058+
pos = smp_load_acquire(&n->pos);
1059+
for (i = 0, k = 0; i < pos; i++) {
10521060
if (!test_bit(i, n->used)) {
10531061
k++;
10541062
continue;
@@ -1062,8 +1070,8 @@ mtype_del(struct ip_set *set, void *value, const struct ip_set_ext *ext,
10621070
ret = 0;
10631071
clear_bit(i, n->used);
10641072
smp_mb__after_atomic();
1065-
if (i + 1 == n->pos)
1066-
n->pos--;
1073+
if (i + 1 == pos)
1074+
smp_store_release(&n->pos, --pos);
10671075
t->hregion[r].elements--;
10681076
#ifdef IP_SET_HASH_WITH_NETS
10691077
for (j = 0; j < IPSET_NET_COUNT; j++)
@@ -1085,11 +1093,11 @@ mtype_del(struct ip_set *set, void *value, const struct ip_set_ext *ext,
10851093
x->flags = flags;
10861094
}
10871095
}
1088-
for (; i < n->pos; i++) {
1096+
for (; i < pos; i++) {
10891097
if (!test_bit(i, n->used))
10901098
k++;
10911099
}
1092-
if (k == n->pos) {
1100+
if (k == pos) {
10931101
t->hregion[r].ext_size -= ext_size(n->size, dsize);
10941102
rcu_assign_pointer(hbucket(t, key), NULL);
10951103
kfree_rcu(n, rcu);
@@ -1100,7 +1108,7 @@ mtype_del(struct ip_set *set, void *value, const struct ip_set_ext *ext,
11001108
if (!tmp)
11011109
goto out;
11021110
tmp->size = n->size - AHASH_INIT_SIZE;
1103-
for (j = 0, k = 0; j < n->pos; j++) {
1111+
for (j = 0, k = 0; j < pos; j++) {
11041112
if (!test_bit(j, n->used))
11051113
continue;
11061114
data = ahash_data(n, j, dsize);
@@ -1161,6 +1169,7 @@ mtype_test_cidrs(struct ip_set *set, struct mtype_elem *d,
11611169
int ret, i, j = 0;
11621170
#endif
11631171
u32 key, multi = 0;
1172+
u8 pos;
11641173

11651174
pr_debug("test by nets\n");
11661175
for (; j < NLEN && h->nets[j].cidr[0] && !multi; j++) {
@@ -1178,7 +1187,8 @@ mtype_test_cidrs(struct ip_set *set, struct mtype_elem *d,
11781187
n = rcu_dereference_bh(hbucket(t, key));
11791188
if (!n)
11801189
continue;
1181-
for (i = 0; i < n->pos; i++) {
1190+
pos = smp_load_acquire(&n->pos);
1191+
for (i = 0; i < pos; i++) {
11821192
if (!test_bit(i, n->used))
11831193
continue;
11841194
data = ahash_data(n, i, set->dsize);
@@ -1212,6 +1222,7 @@ mtype_test(struct ip_set *set, void *value, const struct ip_set_ext *ext,
12121222
struct mtype_elem *data;
12131223
int i, ret = 0;
12141224
u32 key, multi = 0;
1225+
u8 pos;
12151226

12161227
rcu_read_lock_bh();
12171228
t = rcu_dereference_bh(h->table);
@@ -1234,7 +1245,8 @@ mtype_test(struct ip_set *set, void *value, const struct ip_set_ext *ext,
12341245
ret = 0;
12351246
goto out;
12361247
}
1237-
for (i = 0; i < n->pos; i++) {
1248+
pos = smp_load_acquire(&n->pos);
1249+
for (i = 0; i < pos; i++) {
12381250
if (!test_bit(i, n->used))
12391251
continue;
12401252
data = ahash_data(n, i, set->dsize);
@@ -1339,6 +1351,7 @@ mtype_list(const struct ip_set *set,
13391351
/* We assume that one hash bucket fills into one page */
13401352
void *incomplete;
13411353
int i, ret = 0;
1354+
u8 pos;
13421355

13431356
atd = nla_nest_start(skb, IPSET_ATTR_ADT);
13441357
if (!atd)
@@ -1357,7 +1370,8 @@ mtype_list(const struct ip_set *set,
13571370
cb->args[IPSET_CB_ARG0], t, n);
13581371
if (!n)
13591372
continue;
1360-
for (i = 0; i < n->pos; i++) {
1373+
pos = smp_load_acquire(&n->pos);
1374+
for (i = 0; i < pos; i++) {
13611375
if (!test_bit(i, n->used))
13621376
continue;
13631377
e = ahash_data(n, i, set->dsize);

0 commit comments

Comments
 (0)