aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJoerg Marx <joerg.marx@secunet.com>2010-05-20 09:55:30 -0400
committerPatrick McHardy <kaber@trash.net>2010-05-20 09:55:30 -0400
commitfc350777c705a39a312728ac5e8a6f164a828f5d (patch)
tree62aa121cd62e416a505d35de9b5d77ab8ae89f66
parenta1d7c1b4b8dfbc5ecadcff9284d64bb6ad4c0196 (diff)
netfilter: nf_conntrack: fix a race in __nf_conntrack_confirm against nf_ct_get_next_corpse()
This race was triggered by a 'conntrack -F' command running in parallel to the insertion of a hash for a new connection. Losing this race led to a dead conntrack entry effectively blocking traffic for a particular connection until timeout or flushing the conntrack hashes again. Now the check for an already dying connection is done inside the lock. Signed-off-by: Joerg Marx <joerg.marx@secunet.com> Signed-off-by: Patrick McHardy <kaber@trash.net>
-rw-r--r--include/net/netfilter/nf_conntrack_core.h2
-rw-r--r--net/netfilter/nf_conntrack_core.c10
2 files changed, 11 insertions, 1 deletions
diff --git a/include/net/netfilter/nf_conntrack_core.h b/include/net/netfilter/nf_conntrack_core.h
index dffde8e6920e..3d7524fba194 100644
--- a/include/net/netfilter/nf_conntrack_core.h
+++ b/include/net/netfilter/nf_conntrack_core.h
@@ -61,7 +61,7 @@ static inline int nf_conntrack_confirm(struct sk_buff *skb)
61 int ret = NF_ACCEPT; 61 int ret = NF_ACCEPT;
62 62
63 if (ct && ct != &nf_conntrack_untracked) { 63 if (ct && ct != &nf_conntrack_untracked) {
64 if (!nf_ct_is_confirmed(ct) && !nf_ct_is_dying(ct)) 64 if (!nf_ct_is_confirmed(ct))
65 ret = __nf_conntrack_confirm(skb); 65 ret = __nf_conntrack_confirm(skb);
66 if (likely(ret == NF_ACCEPT)) 66 if (likely(ret == NF_ACCEPT))
67 nf_ct_deliver_cached_events(ct); 67 nf_ct_deliver_cached_events(ct);
diff --git a/net/netfilter/nf_conntrack_core.c b/net/netfilter/nf_conntrack_core.c
index b83c530c5e0a..eeeb8bc73982 100644
--- a/net/netfilter/nf_conntrack_core.c
+++ b/net/netfilter/nf_conntrack_core.c
@@ -424,6 +424,16 @@ __nf_conntrack_confirm(struct sk_buff *skb)
424 424
425 spin_lock_bh(&nf_conntrack_lock); 425 spin_lock_bh(&nf_conntrack_lock);
426 426
427 /* We have to check the DYING flag inside the lock to prevent
428 a race against nf_ct_get_next_corpse() possibly called from
429 user context, else we insert an already 'dead' hash, blocking
430 further use of that particular connection -JM */
431
432 if (unlikely(nf_ct_is_dying(ct))) {
433 spin_unlock_bh(&nf_conntrack_lock);
434 return NF_ACCEPT;
435 }
436
427 /* See if there's one in the list already, including reverse: 437 /* See if there's one in the list already, including reverse:
428 NAT could have grabbed it without realizing, since we're 438 NAT could have grabbed it without realizing, since we're
429 not in the hash. If there is, we lost race. */ 439 not in the hash. If there is, we lost race. */