netfilter-devel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: Pablo Neira Ayuso <pablo@netfilter.org>
To: Florian Westphal <fw@strlen.de>
Cc: netfilter-devel@vger.kernel.org
Subject: Re: [PATCH v2 nft] src: add and use refcount assert helper
Date: Tue, 21 Oct 2025 00:08:01 +0200	[thread overview]
Message-ID: <aPaywVyKhzUuj8Mn@calendula> (raw)
In-Reply-To: <20251020072937.1938-1-fw@strlen.de>

On Mon, Oct 20, 2025 at 09:29:34AM +0200, Florian Westphal wrote:
> _get() functions must not be used when refcnt is 0, as expr_free() releases
> expressions on 1 -> 0 transition.
> 
> Also, check that a refcount would not overflow from UINT_MAX to 0.
> Use INT_MAX to also catch refcount leaks sooner, we don't expect 2**31
> get()s on same object.
> 
> This helps catching use-after-free refcounting bugs even when nft is built
> without ASAN support.
> 
> v2: Use a generic helper instead of open-coding.
> 
> Signed-off-by: Florian Westphal <fw@strlen.de>
> ---
>  include/rule.h   |  2 +-
>  include/utils.h  |  7 +++++++
>  src/expression.c |  5 +++++
>  src/rule.c       | 14 ++++++++++++++
>  4 files changed, 27 insertions(+), 1 deletion(-)
> 
> diff --git a/include/rule.h b/include/rule.h
> index 8d2f29d09337..bcdc50cad59d 100644
> --- a/include/rule.h
> +++ b/include/rule.h
> @@ -115,7 +115,7 @@ struct symbol {
>  	struct list_head	list;
>  	const char		*identifier;
>  	struct expr		*expr;
> -	int			refcnt;
> +	unsigned int		refcnt;
>  };
>  
>  extern void symbol_bind(struct scope *scope, const char *identifier,
> diff --git a/include/utils.h b/include/utils.h
> index 474c7595f7cd..3594ce6edf32 100644
> --- a/include/utils.h
> +++ b/include/utils.h
> @@ -6,6 +6,7 @@
>  #include <stdio.h>
>  #include <unistd.h>
>  #include <assert.h>
> +#include <limits.h>
>  #include <list.h>
>  #include <gmputil.h>
>  
> @@ -39,6 +40,12 @@
>  #define __must_be_array(a) \
>  	BUILD_BUG_ON_ZERO(__builtin_types_compatible_p(typeof(a), typeof(&a[0])))
>  
> +static inline void assert_refcount_safe(unsigned int ref)
> +{
> +	assert(ref > 0);
> +	assert(ref < INT_MAX);

Will this point to the right file and line when the assertion is hit?
IIRC we have to use a macro here?

> +}
> +
>  #define container_of(ptr, type, member) ({			\
>  	typeof( ((type *)0)->member ) *__mptr = (ptr);		\
>  	(type *)( (void *)__mptr - offsetof(type,member) );})
> diff --git a/src/expression.c b/src/expression.c
> index 019c263f187b..6c7bebe0a3d1 100644
> --- a/src/expression.c
> +++ b/src/expression.c
> @@ -68,6 +68,7 @@ struct expr *expr_clone(const struct expr *expr)
>  
>  struct expr *expr_get(struct expr *expr)
>  {
> +	assert_refcount_safe(expr->refcnt);
>  	expr->refcnt++;
>  	return expr;
>  }
> @@ -84,6 +85,8 @@ void expr_free(struct expr *expr)
>  {
>  	if (expr == NULL)
>  		return;
> +
> +	assert_refcount_safe(expr->refcnt);
>  	if (--expr->refcnt > 0)
>  		return;
>  
> @@ -343,11 +346,13 @@ static void variable_expr_clone(struct expr *new, const struct expr *expr)
>  	new->scope      = expr->scope;
>  	new->sym	= expr->sym;
>  
> +	assert_refcount_safe(expr->sym->refcnt);
>  	expr->sym->refcnt++;
>  }
>  
>  static void variable_expr_destroy(struct expr *expr)
>  {
> +	assert_refcount_safe(expr->sym->refcnt);
>  	expr->sym->refcnt--;
>  }
>  
> diff --git a/src/rule.c b/src/rule.c
> index d0a62a3ee002..f51d605cc1ad 100644
> --- a/src/rule.c
> +++ b/src/rule.c
> @@ -181,6 +181,7 @@ struct set *set_clone(const struct set *set)
>  
>  struct set *set_get(struct set *set)
>  {
> +	assert_refcount_safe(set->refcnt);
>  	set->refcnt++;
>  	return set;
>  }
> @@ -189,6 +190,7 @@ void set_free(struct set *set)
>  {
>  	struct stmt *stmt, *next;
>  
> +	assert_refcount_safe(set->refcnt);
>  	if (--set->refcnt > 0)
>  		return;
>  
> @@ -484,12 +486,14 @@ struct rule *rule_alloc(const struct location *loc, const struct handle *h)
>  
>  struct rule *rule_get(struct rule *rule)
>  {
> +	assert_refcount_safe(rule->refcnt);
>  	rule->refcnt++;
>  	return rule;
>  }
>  
>  void rule_free(struct rule *rule)
>  {
> +	assert_refcount_safe(rule->refcnt);
>  	if (--rule->refcnt > 0)
>  		return;
>  	stmt_list_free(&rule->stmts);
> @@ -606,6 +610,7 @@ struct symbol *symbol_get(const struct scope *scope, const char *identifier)
>  	if (!sym)
>  		return NULL;
>  
> +	assert_refcount_safe(sym->refcnt);
>  	sym->refcnt++;
>  
>  	return sym;
> @@ -613,6 +618,7 @@ struct symbol *symbol_get(const struct scope *scope, const char *identifier)
>  
>  static void symbol_put(struct symbol *sym)
>  {
> +	assert_refcount_safe(sym->refcnt);
>  	if (--sym->refcnt == 0) {
>  		free_const(sym->identifier);
>  		expr_free(sym->expr);
> @@ -732,6 +738,7 @@ struct chain *chain_alloc(void)
>  
>  struct chain *chain_get(struct chain *chain)
>  {
> +	assert_refcount_safe(chain->refcnt);
>  	chain->refcnt++;
>  	return chain;
>  }
> @@ -741,6 +748,7 @@ void chain_free(struct chain *chain)
>  	struct rule *rule, *next;
>  	int i;
>  
> +	assert_refcount_safe(chain->refcnt);
>  	if (--chain->refcnt > 0)
>  		return;
>  	list_for_each_entry_safe(rule, next, &chain->rules, list)
> @@ -1176,6 +1184,7 @@ void table_free(struct table *table)
>  	struct set *set, *nset;
>  	struct obj *obj, *nobj;
>  
> +	assert_refcount_safe(table->refcnt);
>  	if (--table->refcnt > 0)
>  		return;
>  	if (table->comment)
> @@ -1214,6 +1223,7 @@ void table_free(struct table *table)
>  
>  struct table *table_get(struct table *table)
>  {
> +	assert_refcount_safe(table->refcnt);
>  	table->refcnt++;
>  	return table;
>  }
> @@ -1687,12 +1697,14 @@ struct obj *obj_alloc(const struct location *loc)
>  
>  struct obj *obj_get(struct obj *obj)
>  {
> +	assert_refcount_safe(obj->refcnt);
>  	obj->refcnt++;
>  	return obj;
>  }
>  
>  void obj_free(struct obj *obj)
>  {
> +	assert_refcount_safe(obj->refcnt);
>  	if (--obj->refcnt > 0)
>  		return;
>  	free_const(obj->comment);
> @@ -2270,6 +2282,7 @@ struct flowtable *flowtable_alloc(const struct location *loc)
>  
>  struct flowtable *flowtable_get(struct flowtable *flowtable)
>  {
> +	assert_refcount_safe(flowtable->refcnt);
>  	flowtable->refcnt++;
>  	return flowtable;
>  }
> @@ -2278,6 +2291,7 @@ void flowtable_free(struct flowtable *flowtable)
>  {
>  	int i;
>  
> +	assert_refcount_safe(flowtable->refcnt);
>  	if (--flowtable->refcnt > 0)
>  		return;
>  	handle_free(&flowtable->handle);
> -- 
> 2.51.0
> 
> 

  reply	other threads:[~2025-10-20 22:08 UTC|newest]

Thread overview: 3+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2025-10-20  7:29 [PATCH v2 nft] src: add and use refcount assert helper Florian Westphal
2025-10-20 22:08 ` Pablo Neira Ayuso [this message]
2025-10-20 22:22   ` Florian Westphal

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=aPaywVyKhzUuj8Mn@calendula \
    --to=pablo@netfilter.org \
    --cc=fw@strlen.de \
    --cc=netfilter-devel@vger.kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).