Commit 13ee6ac579574a2a95e982b19920fd2495dce8cd

Authored by Stephen Hemminger
Committed by Pablo Neira Ayuso
1 parent 83723d6071

netfilter: fix race in conntrack between dump_table and destroy

The netlink interface to dump the connection tracking table has a race
when entries are deleted at the same time. A customer reported a crash
and the backtrace showed thatctnetlink_dump_table was running while a
conntrack entry was being destroyed.
(see https://bugzilla.vyatta.com/show_bug.cgi?id=6402).

According to RCU documentation, when using hlist_nulls the reader
must handle the case of seeing a deleted entry and not proceed
further down the linked list.  The old code would continue
which caused the scan to walk into the free list.

This patch uses locking (rather than RCU) for this operation which
is guaranteed safe, and no longer requires getting reference while
doing dump operation.

Signed-off-by: Stephen Hemminger <shemminger@vyatta.com>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>

Showing 1 changed file with 5 additions and 9 deletions Side-by-side Diff

net/netfilter/nf_conntrack_netlink.c
... ... @@ -645,25 +645,23 @@
645 645 struct nfgenmsg *nfmsg = nlmsg_data(cb->nlh);
646 646 u_int8_t l3proto = nfmsg->nfgen_family;
647 647  
648   - rcu_read_lock();
  648 + spin_lock_bh(&nf_conntrack_lock);
649 649 last = (struct nf_conn *)cb->args[1];
650 650 for (; cb->args[0] < net->ct.htable_size; cb->args[0]++) {
651 651 restart:
652   - hlist_nulls_for_each_entry_rcu(h, n, &net->ct.hash[cb->args[0]],
  652 + hlist_nulls_for_each_entry(h, n, &net->ct.hash[cb->args[0]],
653 653 hnnode) {
654 654 if (NF_CT_DIRECTION(h) != IP_CT_DIR_ORIGINAL)
655 655 continue;
656 656 ct = nf_ct_tuplehash_to_ctrack(h);
657   - if (!atomic_inc_not_zero(&ct->ct_general.use))
658   - continue;
659 657 /* Dump entries of a given L3 protocol number.
660 658 * If it is not specified, ie. l3proto == 0,
661 659 * then dump everything. */
662 660 if (l3proto && nf_ct_l3num(ct) != l3proto)
663   - goto releasect;
  661 + continue;
664 662 if (cb->args[1]) {
665 663 if (ct != last)
666   - goto releasect;
  664 + continue;
667 665 cb->args[1] = 0;
668 666 }
669 667 if (ctnetlink_fill_info(skb, NETLINK_CB(cb->skb).pid,
... ... @@ -681,8 +679,6 @@
681 679 if (acct)
682 680 memset(acct, 0, sizeof(struct nf_conn_counter[IP_CT_DIR_MAX]));
683 681 }
684   -releasect:
685   - nf_ct_put(ct);
686 682 }
687 683 if (cb->args[1]) {
688 684 cb->args[1] = 0;
... ... @@ -690,7 +686,7 @@
690 686 }
691 687 }
692 688 out:
693   - rcu_read_unlock();
  689 + spin_unlock_bh(&nf_conntrack_lock);
694 690 if (last)
695 691 nf_ct_put(last);
696 692