netfilter: ctnetlink: fix race between delete and timeout expiration
authorPablo Neira Ayuso <pablo@netfilter.org>
Fri, 16 Mar 2012 02:00:34 +0000 (02:00 +0000)
committerDavid S. Miller <davem@davemloft.net>
Sat, 17 Mar 2012 08:47:08 +0000 (01:47 -0700)
Kerin Millar reported hardlockups while running `conntrackd -c'
in a busy firewall. That system (with several processors) was
acting as backup in a primary-backup setup.

After several tries, I found a race condition between the deletion
operation of ctnetlink and timeout expiration. This patch fixes
this problem.

Tested-by: Kerin Millar <kerframil@gmail.com>
Reported-by: Kerin Millar <kerframil@gmail.com>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
Signed-off-by: David S. Miller <davem@davemloft.net>
net/netfilter/nf_conntrack_netlink.c

index 1068769..b49da6c 100644 (file)
@@ -943,20 +943,21 @@ ctnetlink_del_conntrack(struct sock *ctnl, struct sk_buff *skb,
                }
        }
 
-       if (nf_conntrack_event_report(IPCT_DESTROY, ct,
-                                     NETLINK_CB(skb).pid,
-                                     nlmsg_report(nlh)) < 0) {
+       if (del_timer(&ct->timeout)) {
+               if (nf_conntrack_event_report(IPCT_DESTROY, ct,
+                                             NETLINK_CB(skb).pid,
+                                             nlmsg_report(nlh)) < 0) {
+                       nf_ct_delete_from_lists(ct);
+                       /* we failed to report the event, try later */
+                       nf_ct_insert_dying_list(ct);
+                       nf_ct_put(ct);
+                       return 0;
+               }
+               /* death_by_timeout would report the event again */
+               set_bit(IPS_DYING_BIT, &ct->status);
                nf_ct_delete_from_lists(ct);
-               /* we failed to report the event, try later */
-               nf_ct_insert_dying_list(ct);
                nf_ct_put(ct);
-               return 0;
        }
-
-       /* death_by_timeout would report the event again */
-       set_bit(IPS_DYING_BIT, &ct->status);
-
-       nf_ct_kill(ct);
        nf_ct_put(ct);
 
        return 0;