Skip to content

Commit a2cb4df

Browse files
Florian Westphalgregkh
authored andcommitted
netfilter: ctnetlink: fix refcount leak on table dump
[ Upstream commit de788b2 ] There is a reference count leak in ctnetlink_dump_table(): if (res < 0) { nf_conntrack_get(&ct->ct_general); // HERE cb->args[1] = (unsigned long)ct; ... While its very unlikely, its possible that ct == last. If this happens, then the refcount of ct was already incremented. This 2nd increment is never undone. This prevents the conntrack object from being released, which in turn keeps prevents cnet->count from dropping back to 0. This will then block the netns dismantle (or conntrack rmmod) as nf_conntrack_cleanup_net_list() will wait forever. This can be reproduced by running conntrack_resize.sh selftest in a loop. It takes ~20 minutes for me on a preemptible kernel on average before I see a runaway kworker spinning in nf_conntrack_cleanup_net_list. One fix would to change this to: if (res < 0) { if (ct != last) nf_conntrack_get(&ct->ct_general); But this reference counting isn't needed in the first place. We can just store a cookie value instead. A followup patch will do the same for ctnetlink_exp_dump_table, it looks to me as if this has the same problem and like ctnetlink_dump_table, we only need a 'skip hint', not the actual object so we can apply the same cookie strategy there as well. Fixes: d205dc4 ("[NETFILTER]: ctnetlink: fix deadlock in table dumping") Signed-off-by: Florian Westphal <fw@strlen.de> Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org> Signed-off-by: Sasha Levin <sashal@kernel.org>
1 parent 5e95347 commit a2cb4df

File tree

1 file changed

+13
-11
lines changed

1 file changed

+13
-11
lines changed

net/netfilter/nf_conntrack_netlink.c

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -860,8 +860,6 @@ ctnetlink_conntrack_event(unsigned int events, const struct nf_ct_event *item)
860860

861861
static int ctnetlink_done(struct netlink_callback *cb)
862862
{
863-
if (cb->args[1])
864-
nf_ct_put((struct nf_conn *)cb->args[1]);
865863
kfree(cb->data);
866864
return 0;
867865
}
@@ -1184,19 +1182,26 @@ static int ctnetlink_filter_match(struct nf_conn *ct, void *data)
11841182
return 0;
11851183
}
11861184

1185+
static unsigned long ctnetlink_get_id(const struct nf_conn *ct)
1186+
{
1187+
unsigned long id = nf_ct_get_id(ct);
1188+
1189+
return id ? id : 1;
1190+
}
1191+
11871192
static int
11881193
ctnetlink_dump_table(struct sk_buff *skb, struct netlink_callback *cb)
11891194
{
11901195
unsigned int flags = cb->data ? NLM_F_DUMP_FILTERED : 0;
11911196
struct net *net = sock_net(skb->sk);
1192-
struct nf_conn *ct, *last;
1197+
unsigned long last_id = cb->args[1];
11931198
struct nf_conntrack_tuple_hash *h;
11941199
struct hlist_nulls_node *n;
11951200
struct nf_conn *nf_ct_evict[8];
1201+
struct nf_conn *ct;
11961202
int res, i;
11971203
spinlock_t *lockp;
11981204

1199-
last = (struct nf_conn *)cb->args[1];
12001205
i = 0;
12011206

12021207
local_bh_disable();
@@ -1233,7 +1238,7 @@ ctnetlink_dump_table(struct sk_buff *skb, struct netlink_callback *cb)
12331238
continue;
12341239

12351240
if (cb->args[1]) {
1236-
if (ct != last)
1241+
if (ctnetlink_get_id(ct) != last_id)
12371242
continue;
12381243
cb->args[1] = 0;
12391244
}
@@ -1246,8 +1251,7 @@ ctnetlink_dump_table(struct sk_buff *skb, struct netlink_callback *cb)
12461251
NFNL_MSG_TYPE(cb->nlh->nlmsg_type),
12471252
ct, true, flags);
12481253
if (res < 0) {
1249-
nf_conntrack_get(&ct->ct_general);
1250-
cb->args[1] = (unsigned long)ct;
1254+
cb->args[1] = ctnetlink_get_id(ct);
12511255
spin_unlock(lockp);
12521256
goto out;
12531257
}
@@ -1260,12 +1264,10 @@ ctnetlink_dump_table(struct sk_buff *skb, struct netlink_callback *cb)
12601264
}
12611265
out:
12621266
local_bh_enable();
1263-
if (last) {
1267+
if (last_id) {
12641268
/* nf ct hash resize happened, now clear the leftover. */
1265-
if ((struct nf_conn *)cb->args[1] == last)
1269+
if (cb->args[1] == last_id)
12661270
cb->args[1] = 0;
1267-
1268-
nf_ct_put(last);
12691271
}
12701272

12711273
while (i) {

0 commit comments

Comments
 (0)