Skip to content

Commit 1b4e27f

Browse files
committed
netfilter: ctnetlink: remove refcounting in expectation dumpers
JIRA: https://issues.redhat.com/browse/RHEL-115582 Upstream Status: commit 1492e3d commit 1492e3d Author: Florian Westphal <fw@strlen.de> Date: Fri Aug 1 17:25:09 2025 +0200 netfilter: ctnetlink: remove refcounting in expectation dumpers 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: cf6994c ("[NETFILTER]: nf_conntrack_netlink: sync expectation dumping with conntrack table dumping") Fixes: e844a92 ("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> Signed-off-by: Florian Westphal <fwestpha@redhat.com>
1 parent 33ecc6c commit 1b4e27f

File tree

1 file changed

+17
-24
lines changed

1 file changed

+17
-24
lines changed

net/netfilter/nf_conntrack_netlink.c

Lines changed: 17 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -3171,23 +3171,27 @@ ctnetlink_expect_event(unsigned int events, const struct nf_exp_event *item)
31713171
return 0;
31723172
}
31733173
#endif
3174-
static int ctnetlink_exp_done(struct netlink_callback *cb)
3174+
3175+
static unsigned long ctnetlink_exp_id(const struct nf_conntrack_expect *exp)
31753176
{
3176-
if (cb->args[1])
3177-
nf_ct_expect_put((struct nf_conntrack_expect *)cb->args[1]);
3178-
return 0;
3177+
unsigned long id = (unsigned long)exp;
3178+
3179+
id += nf_ct_get_id(exp->master);
3180+
id += exp->class;
3181+
3182+
return id ? id : 1;
31793183
}
31803184

31813185
static int
31823186
ctnetlink_exp_dump_table(struct sk_buff *skb, struct netlink_callback *cb)
31833187
{
31843188
struct net *net = sock_net(skb->sk);
3185-
struct nf_conntrack_expect *exp, *last;
31863189
struct nfgenmsg *nfmsg = nlmsg_data(cb->nlh);
31873190
u_int8_t l3proto = nfmsg->nfgen_family;
3191+
unsigned long last_id = cb->args[1];
3192+
struct nf_conntrack_expect *exp;
31883193

31893194
rcu_read_lock();
3190-
last = (struct nf_conntrack_expect *)cb->args[1];
31913195
for (; cb->args[0] < nf_ct_expect_hsize; cb->args[0]++) {
31923196
restart:
31933197
hlist_for_each_entry_rcu(exp, &nf_ct_expect_hash[cb->args[0]],
@@ -3199,7 +3203,7 @@ ctnetlink_exp_dump_table(struct sk_buff *skb, struct netlink_callback *cb)
31993203
continue;
32003204

32013205
if (cb->args[1]) {
3202-
if (exp != last)
3206+
if (ctnetlink_exp_id(exp) != last_id)
32033207
continue;
32043208
cb->args[1] = 0;
32053209
}
@@ -3208,9 +3212,7 @@ ctnetlink_exp_dump_table(struct sk_buff *skb, struct netlink_callback *cb)
32083212
cb->nlh->nlmsg_seq,
32093213
IPCTNL_MSG_EXP_NEW,
32103214
exp) < 0) {
3211-
if (!refcount_inc_not_zero(&exp->use))
3212-
continue;
3213-
cb->args[1] = (unsigned long)exp;
3215+
cb->args[1] = ctnetlink_exp_id(exp);
32143216
goto out;
32153217
}
32163218
}
@@ -3221,42 +3223,38 @@ ctnetlink_exp_dump_table(struct sk_buff *skb, struct netlink_callback *cb)
32213223
}
32223224
out:
32233225
rcu_read_unlock();
3224-
if (last)
3225-
nf_ct_expect_put(last);
3226-
32273226
return skb->len;
32283227
}
32293228

32303229
static int
32313230
ctnetlink_exp_ct_dump_table(struct sk_buff *skb, struct netlink_callback *cb)
32323231
{
3233-
struct nf_conntrack_expect *exp, *last;
32343232
struct nfgenmsg *nfmsg = nlmsg_data(cb->nlh);
32353233
struct nf_conn *ct = cb->data;
32363234
struct nf_conn_help *help = nfct_help(ct);
32373235
u_int8_t l3proto = nfmsg->nfgen_family;
3236+
unsigned long last_id = cb->args[1];
3237+
struct nf_conntrack_expect *exp;
32383238

32393239
if (cb->args[0])
32403240
return 0;
32413241

32423242
rcu_read_lock();
3243-
last = (struct nf_conntrack_expect *)cb->args[1];
3243+
32443244
restart:
32453245
hlist_for_each_entry_rcu(exp, &help->expectations, lnode) {
32463246
if (l3proto && exp->tuple.src.l3num != l3proto)
32473247
continue;
32483248
if (cb->args[1]) {
3249-
if (exp != last)
3249+
if (ctnetlink_exp_id(exp) != last_id)
32503250
continue;
32513251
cb->args[1] = 0;
32523252
}
32533253
if (ctnetlink_exp_fill_info(skb, NETLINK_CB(cb->skb).portid,
32543254
cb->nlh->nlmsg_seq,
32553255
IPCTNL_MSG_EXP_NEW,
32563256
exp) < 0) {
3257-
if (!refcount_inc_not_zero(&exp->use))
3258-
continue;
3259-
cb->args[1] = (unsigned long)exp;
3257+
cb->args[1] = ctnetlink_exp_id(exp);
32603258
goto out;
32613259
}
32623260
}
@@ -3267,9 +3265,6 @@ ctnetlink_exp_ct_dump_table(struct sk_buff *skb, struct netlink_callback *cb)
32673265
cb->args[0] = 1;
32683266
out:
32693267
rcu_read_unlock();
3270-
if (last)
3271-
nf_ct_expect_put(last);
3272-
32733268
return skb->len;
32743269
}
32753270

@@ -3288,7 +3283,6 @@ static int ctnetlink_dump_exp_ct(struct net *net, struct sock *ctnl,
32883283
struct nf_conntrack_zone zone;
32893284
struct netlink_dump_control c = {
32903285
.dump = ctnetlink_exp_ct_dump_table,
3291-
.done = ctnetlink_exp_done,
32923286
};
32933287

32943288
err = ctnetlink_parse_tuple(cda, &tuple, CTA_EXPECT_MASTER,
@@ -3338,7 +3332,6 @@ static int ctnetlink_get_expect(struct sk_buff *skb,
33383332
else {
33393333
struct netlink_dump_control c = {
33403334
.dump = ctnetlink_exp_dump_table,
3341-
.done = ctnetlink_exp_done,
33423335
};
33433336
return netlink_dump_start(info->sk, skb, info->nlh, &c);
33443337
}

0 commit comments

Comments
 (0)