forked from Minki/linux
netfilter: xt_hashlimit: fix race that results in duplicated entries
Two packets may race to create the same entry in the hashtable, double check if this packet lost race. This double checking only happens in the path of the packet that creates the hashtable for first time. Note that, with this patch, no packet drops occur if the race happens. Reported-by: Feng Gao <gfree.wind@gmail.com> Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
This commit is contained in:
parent
10db9069eb
commit
09181842b0
@ -157,11 +157,22 @@ dsthash_find(const struct xt_hashlimit_htable *ht,
|
||||
/* allocate dsthash_ent, initialize dst, put in htable and lock it */
|
||||
static struct dsthash_ent *
|
||||
dsthash_alloc_init(struct xt_hashlimit_htable *ht,
|
||||
const struct dsthash_dst *dst)
|
||||
const struct dsthash_dst *dst, bool *race)
|
||||
{
|
||||
struct dsthash_ent *ent;
|
||||
|
||||
spin_lock(&ht->lock);
|
||||
|
||||
/* Two or more packets may race to create the same entry in the
|
||||
* hashtable, double check if this packet lost race.
|
||||
*/
|
||||
ent = dsthash_find(ht, dst);
|
||||
if (ent != NULL) {
|
||||
spin_unlock(&ht->lock);
|
||||
*race = true;
|
||||
return ent;
|
||||
}
|
||||
|
||||
/* initialize hash with random val at the time we allocate
|
||||
* the first hashtable entry */
|
||||
if (unlikely(!ht->rnd_initialized)) {
|
||||
@ -585,6 +596,7 @@ hashlimit_mt(const struct sk_buff *skb, struct xt_action_param *par)
|
||||
unsigned long now = jiffies;
|
||||
struct dsthash_ent *dh;
|
||||
struct dsthash_dst dst;
|
||||
bool race = false;
|
||||
u32 cost;
|
||||
|
||||
if (hashlimit_init_dst(hinfo, &dst, skb, par->thoff) < 0)
|
||||
@ -593,13 +605,18 @@ hashlimit_mt(const struct sk_buff *skb, struct xt_action_param *par)
|
||||
rcu_read_lock_bh();
|
||||
dh = dsthash_find(hinfo, &dst);
|
||||
if (dh == NULL) {
|
||||
dh = dsthash_alloc_init(hinfo, &dst);
|
||||
dh = dsthash_alloc_init(hinfo, &dst, &race);
|
||||
if (dh == NULL) {
|
||||
rcu_read_unlock_bh();
|
||||
goto hotdrop;
|
||||
} else if (race) {
|
||||
/* Already got an entry, update expiration timeout */
|
||||
dh->expires = now + msecs_to_jiffies(hinfo->cfg.expire);
|
||||
rateinfo_recalc(dh, now, hinfo->cfg.mode);
|
||||
} else {
|
||||
dh->expires = jiffies + msecs_to_jiffies(hinfo->cfg.expire);
|
||||
rateinfo_init(dh, hinfo);
|
||||
}
|
||||
dh->expires = jiffies + msecs_to_jiffies(hinfo->cfg.expire);
|
||||
rateinfo_init(dh, hinfo);
|
||||
} else {
|
||||
/* update expiration timeout */
|
||||
dh->expires = now + msecs_to_jiffies(hinfo->cfg.expire);
|
||||
|
Loading…
Reference in New Issue
Block a user