Skip to content

Commit afcb9a4

Browse files
committed
netfilter: ctnetlink: remove refcounting in expectation dumpers
JIRA: https://issues.redhat.com/browse/RHEL-115630 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 e427033 commit afcb9a4

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
@@ -3155,23 +3155,27 @@ ctnetlink_expect_event(unsigned int events, const struct nf_exp_event *item)
31553155
return 0;
31563156
}
31573157
#endif
3158-
static int ctnetlink_exp_done(struct netlink_callback *cb)
3158+
3159+
static unsigned long ctnetlink_exp_id(const struct nf_conntrack_expect *exp)
31593160
{
3160-
if (cb->args[1])
3161-
nf_ct_expect_put((struct nf_conntrack_expect *)cb->args[1]);
3162-
return 0;
3161+
unsigned long id = (unsigned long)exp;
3162+
3163+
id += nf_ct_get_id(exp->master);
3164+
id += exp->class;
3165+
3166+
return id ? id : 1;
31633167
}
31643168

31653169
static int
31663170
ctnetlink_exp_dump_table(struct sk_buff *skb, struct netlink_callback *cb)
31673171
{
31683172
struct net *net = sock_net(skb->sk);
3169-
struct nf_conntrack_expect *exp, *last;
31703173
struct nfgenmsg *nfmsg = nlmsg_data(cb->nlh);
31713174
u_int8_t l3proto = nfmsg->nfgen_family;
3175+
unsigned long last_id = cb->args[1];
3176+
struct nf_conntrack_expect *exp;
31723177

31733178
rcu_read_lock();
3174-
last = (struct nf_conntrack_expect *)cb->args[1];
31753179
for (; cb->args[0] < nf_ct_expect_hsize; cb->args[0]++) {
31763180
restart:
31773181
hlist_for_each_entry_rcu(exp, &nf_ct_expect_hash[cb->args[0]],
@@ -3183,7 +3187,7 @@ ctnetlink_exp_dump_table(struct sk_buff *skb, struct netlink_callback *cb)
31833187
continue;
31843188

31853189
if (cb->args[1]) {
3186-
if (exp != last)
3190+
if (ctnetlink_exp_id(exp) != last_id)
31873191
continue;
31883192
cb->args[1] = 0;
31893193
}
@@ -3192,9 +3196,7 @@ ctnetlink_exp_dump_table(struct sk_buff *skb, struct netlink_callback *cb)
31923196
cb->nlh->nlmsg_seq,
31933197
IPCTNL_MSG_EXP_NEW,
31943198
exp) < 0) {
3195-
if (!refcount_inc_not_zero(&exp->use))
3196-
continue;
3197-
cb->args[1] = (unsigned long)exp;
3199+
cb->args[1] = ctnetlink_exp_id(exp);
31983200
goto out;
31993201
}
32003202
}
@@ -3205,42 +3207,38 @@ ctnetlink_exp_dump_table(struct sk_buff *skb, struct netlink_callback *cb)
32053207
}
32063208
out:
32073209
rcu_read_unlock();
3208-
if (last)
3209-
nf_ct_expect_put(last);
3210-
32113210
return skb->len;
32123211
}
32133212

32143213
static int
32153214
ctnetlink_exp_ct_dump_table(struct sk_buff *skb, struct netlink_callback *cb)
32163215
{
3217-
struct nf_conntrack_expect *exp, *last;
32183216
struct nfgenmsg *nfmsg = nlmsg_data(cb->nlh);
32193217
struct nf_conn *ct = cb->data;
32203218
struct nf_conn_help *help = nfct_help(ct);
32213219
u_int8_t l3proto = nfmsg->nfgen_family;
3220+
unsigned long last_id = cb->args[1];
3221+
struct nf_conntrack_expect *exp;
32223222

32233223
if (cb->args[0])
32243224
return 0;
32253225

32263226
rcu_read_lock();
3227-
last = (struct nf_conntrack_expect *)cb->args[1];
3227+
32283228
restart:
32293229
hlist_for_each_entry_rcu(exp, &help->expectations, lnode) {
32303230
if (l3proto && exp->tuple.src.l3num != l3proto)
32313231
continue;
32323232
if (cb->args[1]) {
3233-
if (exp != last)
3233+
if (ctnetlink_exp_id(exp) != last_id)
32343234
continue;
32353235
cb->args[1] = 0;
32363236
}
32373237
if (ctnetlink_exp_fill_info(skb, NETLINK_CB(cb->skb).portid,
32383238
cb->nlh->nlmsg_seq,
32393239
IPCTNL_MSG_EXP_NEW,
32403240
exp) < 0) {
3241-
if (!refcount_inc_not_zero(&exp->use))
3242-
continue;
3243-
cb->args[1] = (unsigned long)exp;
3241+
cb->args[1] = ctnetlink_exp_id(exp);
32443242
goto out;
32453243
}
32463244
}
@@ -3251,9 +3249,6 @@ ctnetlink_exp_ct_dump_table(struct sk_buff *skb, struct netlink_callback *cb)
32513249
cb->args[0] = 1;
32523250
out:
32533251
rcu_read_unlock();
3254-
if (last)
3255-
nf_ct_expect_put(last);
3256-
32573252
return skb->len;
32583253
}
32593254

@@ -3272,7 +3267,6 @@ static int ctnetlink_dump_exp_ct(struct net *net, struct sock *ctnl,
32723267
struct nf_conntrack_zone zone;
32733268
struct netlink_dump_control c = {
32743269
.dump = ctnetlink_exp_ct_dump_table,
3275-
.done = ctnetlink_exp_done,
32763270
};
32773271

32783272
err = ctnetlink_parse_tuple(cda, &tuple, CTA_EXPECT_MASTER,
@@ -3322,7 +3316,6 @@ static int ctnetlink_get_expect(struct sk_buff *skb,
33223316
else {
33233317
struct netlink_dump_control c = {
33243318
.dump = ctnetlink_exp_dump_table,
3325-
.done = ctnetlink_exp_done,
33263319
};
33273320
return netlink_dump_start(info->sk, skb, info->nlh, &c);
33283321
}

0 commit comments

Comments
 (0)