Skip to content

Commit b36e452

Browse files
Florian Westphalummakynes
authored andcommitted
netfilter: nf_conncount: fix garbage collection confirm race
Yi-Hung Wei and Justin Pettit found a race in the garbage collection scheme used by nf_conncount. When doing list walk, we lookup the tuple in the conntrack table. If the lookup fails we remove this tuple from our list because the conntrack entry is gone. This is the common cause, but turns out its not the only one. The list entry could have been created just before by another cpu, i.e. the conntrack entry might not yet have been inserted into the global hash. The avoid this, we introduce a timestamp and the owning cpu. If the entry appears to be stale, evict only if: 1. The current cpu is the one that added the entry, or, 2. The timestamp is older than two jiffies The second constraint allows GC to be taken over by other cpu too (e.g. because a cpu was offlined or napi got moved to another cpu). We can't pretend the 'doubtful' entry wasn't in our list. Instead, when we don't find an entry indicate via IS_ERR that entry was removed ('did not exist' or withheld ('might-be-unconfirmed'). This most likely also fixes a xt_connlimit imbalance earlier reported by Dmitry Andrianov. Cc: Dmitry Andrianov <dmitry.andrianov@alertme.com> Reported-by: Justin Pettit <jpettit@vmware.com> Reported-by: Yi-Hung Wei <yihung.wei@gmail.com> Signed-off-by: Florian Westphal <fw@strlen.de> Acked-by: Yi-Hung Wei <yihung.wei@gmail.com> Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
1 parent ce00bf0 commit b36e452

File tree

1 file changed

+47
-5
lines changed

1 file changed

+47
-5
lines changed

net/netfilter/nf_conncount.c

Lines changed: 47 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,8 @@ struct nf_conncount_tuple {
4747
struct hlist_node node;
4848
struct nf_conntrack_tuple tuple;
4949
struct nf_conntrack_zone zone;
50+
int cpu;
51+
u32 jiffies32;
5052
};
5153

5254
struct nf_conncount_rb {
@@ -91,30 +93,70 @@ bool nf_conncount_add(struct hlist_head *head,
9193
return false;
9294
conn->tuple = *tuple;
9395
conn->zone = *zone;
96+
conn->cpu = raw_smp_processor_id();
97+
conn->jiffies32 = (u32)jiffies;
9498
hlist_add_head(&conn->node, head);
9599
return true;
96100
}
97101
EXPORT_SYMBOL_GPL(nf_conncount_add);
98102

103+
static const struct nf_conntrack_tuple_hash *
104+
find_or_evict(struct net *net, struct nf_conncount_tuple *conn)
105+
{
106+
const struct nf_conntrack_tuple_hash *found;
107+
unsigned long a, b;
108+
int cpu = raw_smp_processor_id();
109+
__s32 age;
110+
111+
found = nf_conntrack_find_get(net, &conn->zone, &conn->tuple);
112+
if (found)
113+
return found;
114+
b = conn->jiffies32;
115+
a = (u32)jiffies;
116+
117+
/* conn might have been added just before by another cpu and
118+
* might still be unconfirmed. In this case, nf_conntrack_find()
119+
* returns no result. Thus only evict if this cpu added the
120+
* stale entry or if the entry is older than two jiffies.
121+
*/
122+
age = a - b;
123+
if (conn->cpu == cpu || age >= 2) {
124+
hlist_del(&conn->node);
125+
kmem_cache_free(conncount_conn_cachep, conn);
126+
return ERR_PTR(-ENOENT);
127+
}
128+
129+
return ERR_PTR(-EAGAIN);
130+
}
131+
99132
unsigned int nf_conncount_lookup(struct net *net, struct hlist_head *head,
100133
const struct nf_conntrack_tuple *tuple,
101134
const struct nf_conntrack_zone *zone,
102135
bool *addit)
103136
{
104137
const struct nf_conntrack_tuple_hash *found;
105138
struct nf_conncount_tuple *conn;
106-
struct hlist_node *n;
107139
struct nf_conn *found_ct;
140+
struct hlist_node *n;
108141
unsigned int length = 0;
109142

110143
*addit = tuple ? true : false;
111144

112145
/* check the saved connections */
113146
hlist_for_each_entry_safe(conn, n, head, node) {
114-
found = nf_conntrack_find_get(net, &conn->zone, &conn->tuple);
115-
if (found == NULL) {
116-
hlist_del(&conn->node);
117-
kmem_cache_free(conncount_conn_cachep, conn);
147+
found = find_or_evict(net, conn);
148+
if (IS_ERR(found)) {
149+
/* Not found, but might be about to be confirmed */
150+
if (PTR_ERR(found) == -EAGAIN) {
151+
length++;
152+
if (!tuple)
153+
continue;
154+
155+
if (nf_ct_tuple_equal(&conn->tuple, tuple) &&
156+
nf_ct_zone_id(&conn->zone, conn->zone.dir) ==
157+
nf_ct_zone_id(zone, zone->dir))
158+
*addit = false;
159+
}
118160
continue;
119161
}
120162

0 commit comments

Comments
 (0)