All of lore.kernel.org
 help / color / mirror / Atom feed
From: Florian Westphal <fw@strlen.de>
To: <netfilter-devel@vger.kernel.org>
Cc: Florian Westphal <fw@strlen.de>
Subject: [PATCH v2 nft] src: add and use refcount assert helper
Date: Mon, 20 Oct 2025 09:29:34 +0200	[thread overview]
Message-ID: <20251020072937.1938-1-fw@strlen.de> (raw)

_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);
+}
+
 #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  7:29 UTC|newest]

Thread overview: 3+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2025-10-20  7:29 Florian Westphal [this message]
2025-10-20 22:08 ` [PATCH v2 nft] src: add and use refcount assert helper Pablo Neira Ayuso
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=20251020072937.1938-1-fw@strlen.de \
    --to=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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.