Skip to content

Commit ffe8923

Browse files
Florian Westphalummakynes
authored andcommitted
netfilter: nft_compat: make sure xtables destructors have run
Pablo Neira found that after recent update of xt_IDLETIMER the iptables-nft tests sometimes show an error. He tracked this down to the delayed cleanup used by nf_tables core: del rule (transaction A) add rule (transaction B) Its possible that by time transaction B (both in same netns) runs, the xt target destructor has not been invoked yet. For native nft expressions this is no problem because all expressions that have such side effects make sure these are handled from the commit phase, rather than async cleanup. For nft_compat however this isn't true. Instead of forcing synchronous behaviour for nft_compat, keep track of the number of outstanding destructor calls. When we attempt to create a new expression, flush the cleanup worker to make sure destructors have completed. With lots of help from Pablo Neira. Reported-by: Pablo Neira Ayso <[email protected]> Signed-off-by: Florian Westphal <[email protected]> Signed-off-by: Pablo Neira Ayuso <[email protected]>
1 parent 85496a2 commit ffe8923

File tree

3 files changed

+42
-6
lines changed

3 files changed

+42
-6
lines changed

include/net/netfilter/nf_tables.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1498,4 +1498,6 @@ void nft_chain_filter_fini(void);
14981498

14991499
void __init nft_chain_route_init(void);
15001500
void nft_chain_route_fini(void);
1501+
1502+
void nf_tables_trans_destroy_flush_work(void);
15011503
#endif /* _NET_NF_TABLES_H */

net/netfilter/nf_tables_api.c

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7290,6 +7290,12 @@ static void nf_tables_trans_destroy_work(struct work_struct *w)
72907290
}
72917291
}
72927292

7293+
void nf_tables_trans_destroy_flush_work(void)
7294+
{
7295+
flush_work(&trans_destroy_work);
7296+
}
7297+
EXPORT_SYMBOL_GPL(nf_tables_trans_destroy_flush_work);
7298+
72937299
static int nf_tables_commit_chain_prepare(struct net *net, struct nft_chain *chain)
72947300
{
72957301
struct nft_rule *rule;
@@ -7472,9 +7478,9 @@ static void nf_tables_commit_release(struct net *net)
74727478
spin_unlock(&nf_tables_destroy_list_lock);
74737479

74747480
nf_tables_module_autoload_cleanup(net);
7475-
mutex_unlock(&net->nft.commit_mutex);
7476-
74777481
schedule_work(&trans_destroy_work);
7482+
7483+
mutex_unlock(&net->nft.commit_mutex);
74787484
}
74797485

74807486
static int nf_tables_commit(struct net *net, struct sk_buff *skb)

net/netfilter/nft_compat.c

Lines changed: 32 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@ struct nft_xt_match_priv {
2727
void *info;
2828
};
2929

30+
static refcount_t nft_compat_pending_destroy = REFCOUNT_INIT(1);
31+
3032
static int nft_compat_chain_validate_dependency(const struct nft_ctx *ctx,
3133
const char *tablename)
3234
{
@@ -236,6 +238,15 @@ nft_target_init(const struct nft_ctx *ctx, const struct nft_expr *expr,
236238

237239
nft_target_set_tgchk_param(&par, ctx, target, info, &e, proto, inv);
238240

241+
/* xtables matches or targets can have side effects, e.g.
242+
* creation/destruction of /proc files.
243+
* The xt ->destroy functions are run asynchronously from
244+
* work queue. If we have pending invocations we thus
245+
* need to wait for those to finish.
246+
*/
247+
if (refcount_read(&nft_compat_pending_destroy) > 1)
248+
nf_tables_trans_destroy_flush_work();
249+
239250
ret = xt_check_target(&par, size, proto, inv);
240251
if (ret < 0)
241252
return ret;
@@ -247,6 +258,13 @@ nft_target_init(const struct nft_ctx *ctx, const struct nft_expr *expr,
247258
return 0;
248259
}
249260

261+
static void __nft_mt_tg_destroy(struct module *me, const struct nft_expr *expr)
262+
{
263+
refcount_dec(&nft_compat_pending_destroy);
264+
module_put(me);
265+
kfree(expr->ops);
266+
}
267+
250268
static void
251269
nft_target_destroy(const struct nft_ctx *ctx, const struct nft_expr *expr)
252270
{
@@ -262,8 +280,7 @@ nft_target_destroy(const struct nft_ctx *ctx, const struct nft_expr *expr)
262280
if (par.target->destroy != NULL)
263281
par.target->destroy(&par);
264282

265-
module_put(me);
266-
kfree(expr->ops);
283+
__nft_mt_tg_destroy(me, expr);
267284
}
268285

269286
static int nft_extension_dump_info(struct sk_buff *skb, int attr,
@@ -494,8 +511,7 @@ __nft_match_destroy(const struct nft_ctx *ctx, const struct nft_expr *expr,
494511
if (par.match->destroy != NULL)
495512
par.match->destroy(&par);
496513

497-
module_put(me);
498-
kfree(expr->ops);
514+
__nft_mt_tg_destroy(me, expr);
499515
}
500516

501517
static void
@@ -700,6 +716,14 @@ static const struct nfnetlink_subsystem nfnl_compat_subsys = {
700716

701717
static struct nft_expr_type nft_match_type;
702718

719+
static void nft_mt_tg_deactivate(const struct nft_ctx *ctx,
720+
const struct nft_expr *expr,
721+
enum nft_trans_phase phase)
722+
{
723+
if (phase == NFT_TRANS_COMMIT)
724+
refcount_inc(&nft_compat_pending_destroy);
725+
}
726+
703727
static const struct nft_expr_ops *
704728
nft_match_select_ops(const struct nft_ctx *ctx,
705729
const struct nlattr * const tb[])
@@ -738,6 +762,7 @@ nft_match_select_ops(const struct nft_ctx *ctx,
738762
ops->type = &nft_match_type;
739763
ops->eval = nft_match_eval;
740764
ops->init = nft_match_init;
765+
ops->deactivate = nft_mt_tg_deactivate,
741766
ops->destroy = nft_match_destroy;
742767
ops->dump = nft_match_dump;
743768
ops->validate = nft_match_validate;
@@ -828,6 +853,7 @@ nft_target_select_ops(const struct nft_ctx *ctx,
828853
ops->size = NFT_EXPR_SIZE(XT_ALIGN(target->targetsize));
829854
ops->init = nft_target_init;
830855
ops->destroy = nft_target_destroy;
856+
ops->deactivate = nft_mt_tg_deactivate,
831857
ops->dump = nft_target_dump;
832858
ops->validate = nft_target_validate;
833859
ops->data = target;
@@ -891,6 +917,8 @@ static void __exit nft_compat_module_exit(void)
891917
nfnetlink_subsys_unregister(&nfnl_compat_subsys);
892918
nft_unregister_expr(&nft_target_type);
893919
nft_unregister_expr(&nft_match_type);
920+
921+
WARN_ON_ONCE(refcount_read(&nft_compat_pending_destroy) != 1);
894922
}
895923

896924
MODULE_ALIAS_NFNL_SUBSYS(NFNL_SUBSYS_NFT_COMPAT);

0 commit comments

Comments
 (0)