summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJozsef Kadlecsik <kadlec@blackhole.kfki.hu>2015-03-19 13:24:47 +0100
committerJozsef Kadlecsik <kadlec@blackhole.kfki.hu>2015-03-19 13:24:47 +0100
commite2a781b4b8916ee5eff4c29ed5f08790b5ef28d1 (patch)
treeea17470fd41f2c58f69ea9d2a9641b43303b2614
parent1759d7ec38637e1eaf009619f463d6b121e23502 (diff)
Make sure bit operations are not reordered
Sergey Popovich pointed out that {set,clear}_bit() operations must be protected against instruction reordering.
-rw-r--r--kernel/include/linux/netfilter/ipset/ip_set_compat.h.in5
-rw-r--r--kernel/net/netfilter/ipset/ip_set_hash_gen.h7
2 files changed, 10 insertions, 2 deletions
diff --git a/kernel/include/linux/netfilter/ipset/ip_set_compat.h.in b/kernel/include/linux/netfilter/ipset/ip_set_compat.h.in
index 66e830f..a0a7436 100644
--- a/kernel/include/linux/netfilter/ipset/ip_set_compat.h.in
+++ b/kernel/include/linux/netfilter/ipset/ip_set_compat.h.in
@@ -204,6 +204,11 @@ static inline int nla_put_net32(struct sk_buff *skb, int attrtype, __be32 value)
list_entry((pos)->member.prev, typeof(*(pos)), member)
#endif
+#ifndef smp_mb__before_atomic
+#define smp_mb__before_atomic() smp_mb()
+#define smp_mb__after_atomic() smp_mb()
+#endif
+
#ifndef __aligned_u64
#define __aligned_u64 __u64
#endif
diff --git a/kernel/net/netfilter/ipset/ip_set_hash_gen.h b/kernel/net/netfilter/ipset/ip_set_hash_gen.h
index 85cac70..249a7f9 100644
--- a/kernel/net/netfilter/ipset/ip_set_hash_gen.h
+++ b/kernel/net/netfilter/ipset/ip_set_hash_gen.h
@@ -479,6 +479,8 @@ mtype_expire(struct ip_set *set, struct htype *h, u8 nets_length, size_t dsize)
data = ahash_data(n, j, dsize);
if (ip_set_timeout_expired(ext_timeout(data, set))) {
pr_debug("expired %u/%u\n", i, j);
+ clear_bit(j, n->used);
+ smp_mb__after_atomic();
#ifdef IP_SET_HASH_WITH_NETS
for (k = 0; k < IPSET_NET_COUNT; k++)
mtype_del_cidr(h,
@@ -487,7 +489,6 @@ mtype_expire(struct ip_set *set, struct htype *h, u8 nets_length, size_t dsize)
nets_length, k);
#endif
ip_set_ext_destroy(set, data);
- clear_bit(j, n->used);
h->elements--;
d++;
}
@@ -799,9 +800,10 @@ overwrite_extensions:
ip_set_init_comment(ext_comment(data, set), ext);
if (SET_WITH_SKBINFO(set))
ip_set_init_skbinfo(ext_skbinfo(data, set), ext);
- /* Must come last */
+ /* Must come last for the case when timed out entry is reused */
if (SET_WITH_TIMEOUT(set))
ip_set_timeout_set(ext_timeout(data, set), ext->timeout);
+ smp_mb__before_atomic();
set_bit(j, n->used);
if (old != ERR_PTR(-ENOENT)) {
rcu_assign_pointer(hbucket(t, key), n);
@@ -851,6 +853,7 @@ mtype_del(struct ip_set *set, void *value, const struct ip_set_ext *ext,
ret = 0;
clear_bit(i, n->used);
+ smp_mb__after_atomic();
if (i + 1 == n->pos)
n->pos--;
h->elements--;