netfilter: conntrack: fix using __this_cpu_add in preemptible
authorXin Long <lucien.xin@gmail.com>
Thu, 24 Nov 2022 17:21:46 +0000 (12:21 -0500)
committerPablo Neira Ayuso <pablo@netfilter.org>
Wed, 30 Nov 2022 12:08:49 +0000 (13:08 +0100)
Currently in nf_conntrack_hash_check_insert(), when it fails in
nf_ct_ext_valid_pre/post(), NF_CT_STAT_INC() will be called in the
preemptible context, a call trace can be triggered:

   BUG: using __this_cpu_add() in preemptible [00000000] code: conntrack/1636
   caller is nf_conntrack_hash_check_insert+0x45/0x430 [nf_conntrack]
   Call Trace:
    <TASK>
    dump_stack_lvl+0x33/0x46
    check_preemption_disabled+0xc3/0xf0
    nf_conntrack_hash_check_insert+0x45/0x430 [nf_conntrack]
    ctnetlink_create_conntrack+0x3cd/0x4e0 [nf_conntrack_netlink]
    ctnetlink_new_conntrack+0x1c0/0x450 [nf_conntrack_netlink]
    nfnetlink_rcv_msg+0x277/0x2f0 [nfnetlink]
    netlink_rcv_skb+0x50/0x100
    nfnetlink_rcv+0x65/0x144 [nfnetlink]
    netlink_unicast+0x1ae/0x290
    netlink_sendmsg+0x257/0x4f0
    sock_sendmsg+0x5f/0x70

This patch is to fix it by changing to use NF_CT_STAT_INC_ATOMIC() for
nf_ct_ext_valid_pre/post() check in nf_conntrack_hash_check_insert(),
as well as nf_ct_ext_valid_post() in __nf_conntrack_confirm().

Note that nf_ct_ext_valid_pre() check in __nf_conntrack_confirm() is
safe to use NF_CT_STAT_INC(), as it's under local_bh_disable().

Fixes: c56716c69ce1 ("netfilter: extensions: introduce extension genid count")
Signed-off-by: Xin Long <lucien.xin@gmail.com>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
net/netfilter/nf_conntrack_core.c

index 2692139..23b3fed 100644 (file)
@@ -891,7 +891,7 @@ nf_conntrack_hash_check_insert(struct nf_conn *ct)
        zone = nf_ct_zone(ct);
 
        if (!nf_ct_ext_valid_pre(ct->ext)) {
-               NF_CT_STAT_INC(net, insert_failed);
+               NF_CT_STAT_INC_ATOMIC(net, insert_failed);
                return -ETIMEDOUT;
        }
 
@@ -938,7 +938,7 @@ nf_conntrack_hash_check_insert(struct nf_conn *ct)
 
        if (!nf_ct_ext_valid_post(ct->ext)) {
                nf_ct_kill(ct);
-               NF_CT_STAT_INC(net, drop);
+               NF_CT_STAT_INC_ATOMIC(net, drop);
                return -ETIMEDOUT;
        }
 
@@ -1275,7 +1275,7 @@ chaintoolong:
         */
        if (!nf_ct_ext_valid_post(ct->ext)) {
                nf_ct_kill(ct);
-               NF_CT_STAT_INC(net, drop);
+               NF_CT_STAT_INC_ATOMIC(net, drop);
                return NF_DROP;
        }