diff options
author | Florian Westphal <fw@strlen.de> | 2018-06-11 14:20:35 +0300 |
---|---|---|
committer | Pablo Neira Ayuso <pablo@netfilter.org> | 2018-06-12 20:28:18 +0300 |
commit | 71ad00c50d77e507138c792a9646b53c16f22e11 (patch) | |
tree | c1e02e45c573ae8003390fc95923e48cc70440b0 /net/netfilter/nfnetlink.c | |
parent | 215a31f19dedd4e92a67cf5a9717ee898d012b3a (diff) | |
download | linux-71ad00c50d77e507138c792a9646b53c16f22e11.tar.xz |
netfilter: nf_tables: fix module unload race
We must first remove the nfnetlink protocol handler when nf_tables module
is unloaded -- we don't want userspace to submit new change requests once
we've started to tear down nft state.
Furthermore, nfnetlink must not call any subsystem function after
call_batch returned -EAGAIN.
EAGAIN means the subsys mutex was dropped, so its unlikely but possible that
nf_tables subsystem was removed due to 'rmmod nf_tables' on another cpu.
Therefore, we must abort batch completely and not move on to next part of
the batch.
Last, we can't invoke ->abort unless we've checked that the subsystem is
still registered.
Change netns exit path of nf_tables to make sure any incompleted
transaction gets removed on exit.
Signed-off-by: Florian Westphal <fw@strlen.de>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
Diffstat (limited to 'net/netfilter/nfnetlink.c')
-rw-r--r-- | net/netfilter/nfnetlink.c | 10 |
1 files changed, 7 insertions, 3 deletions
diff --git a/net/netfilter/nfnetlink.c b/net/netfilter/nfnetlink.c index 4d0da7042aff..e1b6be29848d 100644 --- a/net/netfilter/nfnetlink.c +++ b/net/netfilter/nfnetlink.c @@ -429,7 +429,7 @@ replay: */ if (err == -EAGAIN) { status |= NFNL_BATCH_REPLAY; - goto next; + goto done; } } ack: @@ -456,7 +456,7 @@ ack: if (err) status |= NFNL_BATCH_FAILURE; } -next: + msglen = NLMSG_ALIGN(nlh->nlmsg_len); if (msglen > skb->len) msglen = skb->len; @@ -464,7 +464,11 @@ next: } done: if (status & NFNL_BATCH_REPLAY) { - ss->abort(net, oskb); + const struct nfnetlink_subsystem *ss2; + + ss2 = nfnl_dereference_protected(subsys_id); + if (ss2 == ss) + ss->abort(net, oskb); nfnl_err_reset(&err_list); nfnl_unlock(subsys_id); kfree_skb(skb); |