| From 5c8ec910e789a92229978d8fd1fce7b62e8ac711 Mon Sep 17 00:00:00 2001 |
| From: Patrick McHardy <kaber@trash.net> |
| Date: Mon, 22 Jun 2009 14:14:16 +0200 |
| Subject: netfilter: nf_conntrack: fix confirmation race condition |
| |
| From: Patrick McHardy <kaber@trash.net> |
| |
| commit 5c8ec910e789a92229978d8fd1fce7b62e8ac711 upstream. |
| |
| New connection tracking entries are inserted into the hash before they |
| are fully set up, namely the CONFIRMED bit is not set and the timer not |
| started yet. This can theoretically lead to a race with timer, which |
| would set the timeout value to a relative value, most likely already in |
| the past. |
| |
| Perform hash insertion as the final step to fix this. |
| |
| Signed-off-by: Patrick McHardy <kaber@trash.net> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> |
| |
| --- |
| net/netfilter/nf_conntrack_core.c | 9 ++++++++- |
| 1 file changed, 8 insertions(+), 1 deletion(-) |
| |
| --- a/net/netfilter/nf_conntrack_core.c |
| +++ b/net/netfilter/nf_conntrack_core.c |
| @@ -385,7 +385,6 @@ __nf_conntrack_confirm(struct sk_buff *s |
| /* Remove from unconfirmed list */ |
| hlist_nulls_del_rcu(&ct->tuplehash[IP_CT_DIR_ORIGINAL].hnnode); |
| |
| - __nf_conntrack_hash_insert(ct, hash, repl_hash); |
| /* Timer relative to confirmation time, not original |
| setting time, otherwise we'd get timer wrap in |
| weird delay cases. */ |
| @@ -393,8 +392,16 @@ __nf_conntrack_confirm(struct sk_buff *s |
| add_timer(&ct->timeout); |
| atomic_inc(&ct->ct_general.use); |
| set_bit(IPS_CONFIRMED_BIT, &ct->status); |
| + |
| + /* Since the lookup is lockless, hash insertion must be done after |
| + * starting the timer and setting the CONFIRMED bit. The RCU barriers |
| + * guarantee that no other CPU can find the conntrack before the above |
| + * stores are visible. |
| + */ |
| + __nf_conntrack_hash_insert(ct, hash, repl_hash); |
| NF_CT_STAT_INC(net, insert); |
| spin_unlock_bh(&nf_conntrack_lock); |
| + |
| help = nfct_help(ct); |
| if (help && help->helper) |
| nf_conntrack_event_cache(IPCT_HELPER, ct); |