diff options
author | bill bonaparte <programme110@gmail.com> | 2014-11-06 08:36:48 -0500 |
---|---|---|
committer | Pablo Neira Ayuso <pablo@netfilter.org> | 2014-11-14 11:43:05 -0500 |
commit | 5195c14c8b27cc0b18220ddbf0e5ad3328a04187 (patch) | |
tree | 4cb248fa4103774404e2d8b50cb1c230e694dfaa | |
parent | b326dd37b94e29bf6a15940f4fa66aa21a678ab1 (diff) |
netfilter: conntrack: fix race in __nf_conntrack_confirm against get_next_corpse
After removal of the central spinlock nf_conntrack_lock, in
commit 93bb0ceb75be2 ("netfilter: conntrack: remove central
spinlock nf_conntrack_lock"), it is possible to race against
get_next_corpse().
The race is against the get_next_corpse() cleanup on
the "unconfirmed" list (a per-cpu list with seperate locking),
which set the DYING bit.
Fix this race, in __nf_conntrack_confirm(), by removing the CT
from unconfirmed list before checking the DYING bit. In case
race occured, re-add the CT to the dying list.
While at this, fix coding style of the comment that has been
updated.
Fixes: 93bb0ceb75be2 ("netfilter: conntrack: remove central spinlock nf_conntrack_lock")
Reported-by: bill bonaparte <programme110@gmail.com>
Signed-off-by: bill bonaparte <programme110@gmail.com>
Signed-off-by: Jesper Dangaard Brouer <brouer@redhat.com>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
-rw-r--r-- | net/netfilter/nf_conntrack_core.c | 14 |
1 files changed, 8 insertions, 6 deletions
diff --git a/net/netfilter/nf_conntrack_core.c b/net/netfilter/nf_conntrack_core.c index 5016a6929085..2c699757bccf 100644 --- a/net/netfilter/nf_conntrack_core.c +++ b/net/netfilter/nf_conntrack_core.c | |||
@@ -611,12 +611,16 @@ __nf_conntrack_confirm(struct sk_buff *skb) | |||
611 | */ | 611 | */ |
612 | NF_CT_ASSERT(!nf_ct_is_confirmed(ct)); | 612 | NF_CT_ASSERT(!nf_ct_is_confirmed(ct)); |
613 | pr_debug("Confirming conntrack %p\n", ct); | 613 | pr_debug("Confirming conntrack %p\n", ct); |
614 | /* We have to check the DYING flag inside the lock to prevent | 614 | |
615 | a race against nf_ct_get_next_corpse() possibly called from | 615 | /* We have to check the DYING flag after unlink to prevent |
616 | user context, else we insert an already 'dead' hash, blocking | 616 | * a race against nf_ct_get_next_corpse() possibly called from |
617 | further use of that particular connection -JM */ | 617 | * user context, else we insert an already 'dead' hash, blocking |
618 | * further use of that particular connection -JM. | ||
619 | */ | ||
620 | nf_ct_del_from_dying_or_unconfirmed_list(ct); | ||
618 | 621 | ||
619 | if (unlikely(nf_ct_is_dying(ct))) { | 622 | if (unlikely(nf_ct_is_dying(ct))) { |
623 | nf_ct_add_to_dying_list(ct); | ||
620 | nf_conntrack_double_unlock(hash, reply_hash); | 624 | nf_conntrack_double_unlock(hash, reply_hash); |
621 | local_bh_enable(); | 625 | local_bh_enable(); |
622 | return NF_ACCEPT; | 626 | return NF_ACCEPT; |
@@ -636,8 +640,6 @@ __nf_conntrack_confirm(struct sk_buff *skb) | |||
636 | zone == nf_ct_zone(nf_ct_tuplehash_to_ctrack(h))) | 640 | zone == nf_ct_zone(nf_ct_tuplehash_to_ctrack(h))) |
637 | goto out; | 641 | goto out; |
638 | 642 | ||
639 | nf_ct_del_from_dying_or_unconfirmed_list(ct); | ||
640 | |||
641 | /* Timer relative to confirmation time, not original | 643 | /* Timer relative to confirmation time, not original |
642 | setting time, otherwise we'd get timer wrap in | 644 | setting time, otherwise we'd get timer wrap in |
643 | weird delay cases. */ | 645 | weird delay cases. */ |