]> www.infradead.org Git - users/jedix/linux-maple.git/commitdiff
netfilter: ctnetlink: remove refcounting in expectation dumpers
authorFlorian Westphal <fw@strlen.de>
Fri, 1 Aug 2025 15:25:09 +0000 (17:25 +0200)
committerPablo Neira Ayuso <pablo@netfilter.org>
Thu, 7 Aug 2025 11:19:26 +0000 (13:19 +0200)
Same pattern as previous patch: do not keep the expectation object
alive via refcount, only store a cookie value and then use that
as the skip hint for dump resumption.

AFAICS this has the same issue as the one resolved in the conntrack
dumper, when we do
  if (!refcount_inc_not_zero(&exp->use))

to increment the refcount, there is a chance that exp == last, which
causes a double-increment of the refcount and subsequent memory leak.

Fixes: cf6994c2b981 ("[NETFILTER]: nf_conntrack_netlink: sync expectation dumping with conntrack table dumping")
Fixes: e844a928431f ("netfilter: ctnetlink: allow to dump expectation per master conntrack")
Signed-off-by: Florian Westphal <fw@strlen.de>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
net/netfilter/nf_conntrack_netlink.c

index f403acd824372c41c9833139078b643121ab66da..50fd6809380fa95a2cd93a7a84ecc9800fb662d6 100644 (file)
@@ -3170,23 +3170,27 @@ errout:
        return 0;
 }
 #endif
-static int ctnetlink_exp_done(struct netlink_callback *cb)
+
+static unsigned long ctnetlink_exp_id(const struct nf_conntrack_expect *exp)
 {
-       if (cb->args[1])
-               nf_ct_expect_put((struct nf_conntrack_expect *)cb->args[1]);
-       return 0;
+       unsigned long id = (unsigned long)exp;
+
+       id += nf_ct_get_id(exp->master);
+       id += exp->class;
+
+       return id ? id : 1;
 }
 
 static int
 ctnetlink_exp_dump_table(struct sk_buff *skb, struct netlink_callback *cb)
 {
        struct net *net = sock_net(skb->sk);
-       struct nf_conntrack_expect *exp, *last;
        struct nfgenmsg *nfmsg = nlmsg_data(cb->nlh);
        u_int8_t l3proto = nfmsg->nfgen_family;
+       unsigned long last_id = cb->args[1];
+       struct nf_conntrack_expect *exp;
 
        rcu_read_lock();
-       last = (struct nf_conntrack_expect *)cb->args[1];
        for (; cb->args[0] < nf_ct_expect_hsize; cb->args[0]++) {
 restart:
                hlist_for_each_entry_rcu(exp, &nf_ct_expect_hash[cb->args[0]],
@@ -3198,7 +3202,7 @@ restart:
                                continue;
 
                        if (cb->args[1]) {
-                               if (exp != last)
+                               if (ctnetlink_exp_id(exp) != last_id)
                                        continue;
                                cb->args[1] = 0;
                        }
@@ -3207,9 +3211,7 @@ restart:
                                                    cb->nlh->nlmsg_seq,
                                                    IPCTNL_MSG_EXP_NEW,
                                                    exp) < 0) {
-                               if (!refcount_inc_not_zero(&exp->use))
-                                       continue;
-                               cb->args[1] = (unsigned long)exp;
+                               cb->args[1] = ctnetlink_exp_id(exp);
                                goto out;
                        }
                }
@@ -3220,32 +3222,30 @@ restart:
        }
 out:
        rcu_read_unlock();
-       if (last)
-               nf_ct_expect_put(last);
-
        return skb->len;
 }
 
 static int
 ctnetlink_exp_ct_dump_table(struct sk_buff *skb, struct netlink_callback *cb)
 {
-       struct nf_conntrack_expect *exp, *last;
        struct nfgenmsg *nfmsg = nlmsg_data(cb->nlh);
        struct nf_conn *ct = cb->data;
        struct nf_conn_help *help = nfct_help(ct);
        u_int8_t l3proto = nfmsg->nfgen_family;
+       unsigned long last_id = cb->args[1];
+       struct nf_conntrack_expect *exp;
 
        if (cb->args[0])
                return 0;
 
        rcu_read_lock();
-       last = (struct nf_conntrack_expect *)cb->args[1];
+
 restart:
        hlist_for_each_entry_rcu(exp, &help->expectations, lnode) {
                if (l3proto && exp->tuple.src.l3num != l3proto)
                        continue;
                if (cb->args[1]) {
-                       if (exp != last)
+                       if (ctnetlink_exp_id(exp) != last_id)
                                continue;
                        cb->args[1] = 0;
                }
@@ -3253,9 +3253,7 @@ restart:
                                            cb->nlh->nlmsg_seq,
                                            IPCTNL_MSG_EXP_NEW,
                                            exp) < 0) {
-                       if (!refcount_inc_not_zero(&exp->use))
-                               continue;
-                       cb->args[1] = (unsigned long)exp;
+                       cb->args[1] = ctnetlink_exp_id(exp);
                        goto out;
                }
        }
@@ -3266,9 +3264,6 @@ restart:
        cb->args[0] = 1;
 out:
        rcu_read_unlock();
-       if (last)
-               nf_ct_expect_put(last);
-
        return skb->len;
 }
 
@@ -3287,7 +3282,6 @@ static int ctnetlink_dump_exp_ct(struct net *net, struct sock *ctnl,
        struct nf_conntrack_zone zone;
        struct netlink_dump_control c = {
                .dump = ctnetlink_exp_ct_dump_table,
-               .done = ctnetlink_exp_done,
        };
 
        err = ctnetlink_parse_tuple(cda, &tuple, CTA_EXPECT_MASTER,
@@ -3337,7 +3331,6 @@ static int ctnetlink_get_expect(struct sk_buff *skb,
                else {
                        struct netlink_dump_control c = {
                                .dump = ctnetlink_exp_dump_table,
-                               .done = ctnetlink_exp_done,
                        };
                        return netlink_dump_start(info->sk, skb, info->nlh, &c);
                }