Re: [PATCH nf 2/2] netfilter: nft_compat: protect lists between select_ops and init

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Taehee Yoo <ap420073@xxxxxxxxx> wrote:
> index 83adabdec900..8f3114871d20 100644
> --- a/net/netfilter/nft_compat.c
> +++ b/net/netfilter/nft_compat.c
> @@ -29,6 +29,7 @@ struct nft_xt {
>  	struct list_head	head;
>  	struct nft_expr_ops	ops;
>  	unsigned int		refcnt;
> +	bool			use;
>  
>  	/* Unlike other expressions, ops doesn't have static storage duration.
>  	 * nft core assumes they do.  We use kfree_rcu so that nft core can
> @@ -48,7 +49,7 @@ struct nft_xt_match_priv {
>  static bool nft_xt_put(struct nft_xt *xt)
>  {
>  	mutex_lock(&nft_xt_lock);
> -	if (--xt->refcnt == 0) {
> +	if (--xt->refcnt == 0 && !xt->use) {
>  		list_del(&xt->head);

I don't doubt your findings, but I would prefer if we do not have to add
these kinds of conditionals here.

The problem is that when nft_xt_put is called, all side
effects are supposed to have been completed.

At this point, list_del() should not be allowed anymore,
it should have taken place while we were still under transaction
mutex.

Do you see any issues with this alternate approach?
It adds deactivate hook to list_del, so after transaction mutex is
released, next 'add' will create a new compat expression rather
than trying to use the one that is scheduled for free.

diff --git a/net/netfilter/nft_compat.c b/net/netfilter/nft_compat.c
--- a/net/netfilter/nft_compat.c
+++ b/net/netfilter/nft_compat.c
@@ -27,6 +27,7 @@ struct nft_xt {
 	struct list_head	head;
 	struct nft_expr_ops	ops;
 	unsigned int		refcnt;
+	unsigned int		listcnt;
 
 	/* Unlike other expressions, ops doesn't have static storage duration.
 	 * nft core assumes they do.  We use kfree_rcu so that nft core can
@@ -43,10 +44,15 @@ struct nft_xt_match_priv {
 	void *info;
 };
 
+static LIST_HEAD(nft_match_list);
+static LIST_HEAD(nft_target_list);
+static struct nft_expr_type nft_match_type;
+static struct nft_expr_type nft_target_type;
+
 static bool nft_xt_put(struct nft_xt *xt)
 {
 	if (--xt->refcnt == 0) {
-		list_del(&xt->head);
+		WARN_ON_ONCE(!list_empty(&xt->head));
 		kfree_rcu(xt, rcu_head);
 		return true;
 	}
@@ -540,6 +546,40 @@ nft_match_destroy(const struct nft_ctx *ctx, const struct nft_expr *expr)
 	__nft_match_destroy(ctx, expr, nft_expr_priv(expr));
 }
 
+static void nft_compat_activate(const struct nft_ctx *ctx,
+				const struct nft_expr *expr,
+				struct list_head *h)
+{
+	struct nft_xt *xt = container_of(expr->ops, struct nft_xt, ops);
+
+	if (xt->listcnt == 0)
+		list_add(&xt->head, h);
+
+	xt->listcnt++;
+}
+
+static void nft_compat_activate_mt(const struct nft_ctx *ctx,
+				   const struct nft_expr *expr)
+{
+	nft_compat_activate(ctx, expr, &nft_match_list);
+}
+
+static void nft_compat_activate_tg(const struct nft_ctx *ctx,
+				   const struct nft_expr *expr)
+{
+	nft_compat_activate(ctx, expr, &nft_target_list);
+}
+
+static void nft_compat_deactivate(const struct nft_ctx *ctx,
+				  const struct nft_expr *expr)
+{
+	struct nft_xt *xt = container_of(expr->ops, struct nft_xt, ops);
+
+	WARN_ON_ONCE(xt->refcnt == 0);
+	if (--xt->listcnt == 0)
+		list_del_init(&xt->head);
+}
+
 static void
 nft_match_large_destroy(const struct nft_ctx *ctx, const struct nft_expr *expr)
 {
@@ -734,10 +774,6 @@ static const struct nfnetlink_subsystem nfnl_compat_subsys = {
 	.cb		= nfnl_nft_compat_cb,
 };
 
-static LIST_HEAD(nft_match_list);
-
-static struct nft_expr_type nft_match_type;
-
 static bool nft_match_cmp(const struct xt_match *match,
 			  const char *name, u32 rev, u32 family)
 {
@@ -794,6 +830,8 @@ nft_match_select_ops(const struct nft_ctx *ctx,
 	nft_match->ops.eval = nft_match_eval;
 	nft_match->ops.init = nft_match_init;
 	nft_match->ops.destroy = nft_match_destroy;
+	nft_match->ops.activate = nft_compat_activate_mt;
+	nft_match->ops.deactivate = nft_compat_deactivate;
 	nft_match->ops.dump = nft_match_dump;
 	nft_match->ops.validate = nft_match_validate;
 	nft_match->ops.data = match;
@@ -810,6 +848,7 @@ nft_match_select_ops(const struct nft_ctx *ctx,
 
 	nft_match->ops.size = matchsize;
 
+	nft_match->listcnt = 1;
 	list_add(&nft_match->head, &nft_match_list);
 
 	return &nft_match->ops;
@@ -826,10 +865,6 @@ static struct nft_expr_type nft_match_type __read_mostly = {
 	.owner		= THIS_MODULE,
 };
 
-static LIST_HEAD(nft_target_list);
-
-static struct nft_expr_type nft_target_type;
-
 static bool nft_target_cmp(const struct xt_target *tg,
 			   const char *name, u32 rev, u32 family)
 {
@@ -898,6 +933,8 @@ nft_target_select_ops(const struct nft_ctx *ctx,
 	nft_target->ops.size = NFT_EXPR_SIZE(XT_ALIGN(target->targetsize));
 	nft_target->ops.init = nft_target_init;
 	nft_target->ops.destroy = nft_target_destroy;
+	nft_target->ops.activate = nft_compat_activate_tg;
+	nft_target->ops.deactivate = nft_compat_deactivate;
 	nft_target->ops.dump = nft_target_dump;
 	nft_target->ops.validate = nft_target_validate;
 	nft_target->ops.data = target;
@@ -907,6 +944,7 @@ nft_target_select_ops(const struct nft_ctx *ctx,
 	else
 		nft_target->ops.eval = nft_target_eval_xt;
 
+	nft_target->listcnt = 1;
 	list_add(&nft_target->head, &nft_target_list);
 
 	return &nft_target->ops;



[Index of Archives]     [Netfitler Users]     [LARTC]     [Bugtraq]     [Yosemite Forum]

  Powered by Linux