Skip to content

Commit 5c92dbf

Browse files
committed
netfilter: conntrack: fix rmmod double-free race
Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=2189550 Upstream Status: commit e6d57e9 commit e6d57e9 Author: Florian Westphal <fw@strlen.de> Date: Tue Feb 14 17:23:59 2023 +0100 netfilter: conntrack: fix rmmod double-free race nf_conntrack_hash_check_insert() callers free the ct entry directly, via nf_conntrack_free. This isn't safe anymore because nf_conntrack_hash_check_insert() might place the entry into the conntrack table and then delteted the entry again because it found that a conntrack extension has been removed at the same time. In this case, the just-added entry is removed again and an error is returned to the caller. Problem is that another cpu might have picked up this entry and incremented its reference count. This results in a use-after-free/double-free, once by the other cpu and once by the caller of nf_conntrack_hash_check_insert(). Fix this by making nf_conntrack_hash_check_insert() not fail anymore after the insertion, just like before the 'Fixes' commit. This is safe because a racing nf_ct_iterate() has to wait for us to release the conntrack hash spinlocks. While at it, make the function return -EAGAIN in the rmmod (genid changed) case, this makes nfnetlink replay the command (suggested by Pablo Neira). Fixes: c56716c ("netfilter: extensions: introduce extension genid count") 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 e9d8ea1 commit 5c92dbf

File tree

3 files changed

+15
-14
lines changed

3 files changed

+15
-14
lines changed

net/netfilter/nf_conntrack_bpf.c

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -381,7 +381,6 @@ struct nf_conn *bpf_ct_insert_entry(struct nf_conn___init *nfct_i)
381381
struct nf_conn *nfct = (struct nf_conn *)nfct_i;
382382
int err;
383383

384-
nfct->status |= IPS_CONFIRMED;
385384
err = nf_conntrack_hash_check_insert(nfct);
386385
if (err < 0) {
387386
nf_conntrack_free(nfct);

net/netfilter/nf_conntrack_core.c

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -890,10 +890,8 @@ nf_conntrack_hash_check_insert(struct nf_conn *ct)
890890

891891
zone = nf_ct_zone(ct);
892892

893-
if (!nf_ct_ext_valid_pre(ct->ext)) {
894-
NF_CT_STAT_INC_ATOMIC(net, insert_failed);
895-
return -ETIMEDOUT;
896-
}
893+
if (!nf_ct_ext_valid_pre(ct->ext))
894+
return -EAGAIN;
897895

898896
local_bh_disable();
899897
do {
@@ -928,6 +926,19 @@ nf_conntrack_hash_check_insert(struct nf_conn *ct)
928926
goto chaintoolong;
929927
}
930928

929+
/* If genid has changed, we can't insert anymore because ct
930+
* extensions could have stale pointers and nf_ct_iterate_destroy
931+
* might have completed its table scan already.
932+
*
933+
* Increment of the ext genid right after this check is fine:
934+
* nf_ct_iterate_destroy blocks until locks are released.
935+
*/
936+
if (!nf_ct_ext_valid_post(ct->ext)) {
937+
err = -EAGAIN;
938+
goto out;
939+
}
940+
941+
ct->status |= IPS_CONFIRMED;
931942
smp_wmb();
932943
/* The caller holds a reference to this object */
933944
refcount_set(&ct->ct_general.use, 2);
@@ -936,12 +947,6 @@ nf_conntrack_hash_check_insert(struct nf_conn *ct)
936947
NF_CT_STAT_INC(net, insert);
937948
local_bh_enable();
938949

939-
if (!nf_ct_ext_valid_post(ct->ext)) {
940-
nf_ct_kill(ct);
941-
NF_CT_STAT_INC_ATOMIC(net, drop);
942-
return -ETIMEDOUT;
943-
}
944-
945950
return 0;
946951
chaintoolong:
947952
NF_CT_STAT_INC(net, chaintoolong);

net/netfilter/nf_conntrack_netlink.c

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2321,9 +2321,6 @@ ctnetlink_create_conntrack(struct net *net,
23212321
nfct_seqadj_ext_add(ct);
23222322
nfct_synproxy_ext_add(ct);
23232323

2324-
/* we must add conntrack extensions before confirmation. */
2325-
ct->status |= IPS_CONFIRMED;
2326-
23272324
if (cda[CTA_STATUS]) {
23282325
err = ctnetlink_change_status(ct, cda);
23292326
if (err < 0)

0 commit comments

Comments
 (0)