diff mbox series

[nf-next,3/9] netfilter: nf_tables: add lockdep assertion for chain use counter

Message ID 20240307084018.2219-4-fw@strlen.de
State Deferred
Headers show
Series netfilter: nf_tables: rewrite gc again | expand

Commit Message

Florian Westphal March 7, 2024, 8:40 a.m. UTC
nft_set_elem_destroy() callers need to hold the transaction mutex
for maps holding chain or object references.

This helper is also called from the nft_dynset error path,
without mutex.

nft_dynset doesn't support verdict or objref maps, however.

Signed-off-by: Florian Westphal <fw@strlen.de>
---
 net/netfilter/nf_tables_api.c | 21 ++++++++++++++++++++-
 1 file changed, 20 insertions(+), 1 deletion(-)
diff mbox series

Patch

diff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c
index bac5847a5499..d6448d6e9a18 100644
--- a/net/netfilter/nf_tables_api.c
+++ b/net/netfilter/nf_tables_api.c
@@ -6404,7 +6404,19 @@  static void nft_set_elem_expr_destroy(const struct nft_ctx *ctx,
 		__nft_set_elem_expr_destroy(ctx, expr);
 }
 
-/* Drop references and destroy. Called from gc, dynset and abort path. */
+/**
+ * nft_set_elem_destroy - free a set element instantly
+ * @set: the set the element was supposed to be added to
+ * @elem: the private element pointer to be free'd
+ * @destroy_expr: true if embedded expression was initialised before
+ *
+ * Immediately releases an element without going through any synchronization.
+ * This function can only be used for error unwinding BEFORE the element was
+ * added to the set, else concurrent data path access may result in
+ * use-after-free.
+ * For datapath error unwinding, jumps-to-chain or objref are
+ * not supported.
+ */
 void nft_set_elem_destroy(const struct nft_set *set,
 			  const struct nft_elem_priv *elem_priv,
 			  bool destroy_expr)
@@ -6415,6 +6427,13 @@  void nft_set_elem_destroy(const struct nft_set *set,
 		.family	= set->table->family,
 	};
 
+	/* We can only do error unwind for vmaps or objref types
+	 * if the caller is holding the transaction mutex.
+	 */
+	if (set->dtype == NFT_DATA_VERDICT ||
+	    nft_set_ext_exists(ext, NFT_SET_EXT_OBJREF))
+		WARN_ON_ONCE(!lockdep_commit_lock_is_held(read_pnet(&set->net)));
+
 	nft_data_release(nft_set_ext_key(ext), NFT_DATA_VALUE);
 	if (nft_set_ext_exists(ext, NFT_SET_EXT_DATA))
 		nft_data_release(nft_set_ext_data(ext), set->dtype);