{"id":2225338,"url":"http://patchwork.ozlabs.org/api/1.1/patches/2225338/?format=json","web_url":"http://patchwork.ozlabs.org/project/netfilter-devel/patch/20260420211105.55177-1-pablo@netfilter.org/","project":{"id":26,"url":"http://patchwork.ozlabs.org/api/1.1/projects/26/?format=json","name":"Netfilter Development","link_name":"netfilter-devel","list_id":"netfilter-devel.vger.kernel.org","list_email":"netfilter-devel@vger.kernel.org","web_url":null,"scm_url":null,"webscm_url":null},"msgid":"<20260420211105.55177-1-pablo@netfilter.org>","date":"2026-04-20T21:11:04","name":"[nf,v2] netfilter: nft_compat: run checkentry() from .validate","commit_ref":null,"pull_url":null,"state":"superseded","archived":true,"hash":"b02a6f705ff86e5a55166fc82e99358745cf8a4d","submitter":{"id":1315,"url":"http://patchwork.ozlabs.org/api/1.1/people/1315/?format=json","name":"Pablo Neira Ayuso","email":"pablo@netfilter.org"},"delegate":{"id":11902,"url":"http://patchwork.ozlabs.org/api/1.1/users/11902/?format=json","username":"strlen","first_name":"Florian","last_name":"Westphal","email":"fw@strlen.de"},"mbox":"http://patchwork.ozlabs.org/project/netfilter-devel/patch/20260420211105.55177-1-pablo@netfilter.org/mbox/","series":[{"id":500684,"url":"http://patchwork.ozlabs.org/api/1.1/series/500684/?format=json","web_url":"http://patchwork.ozlabs.org/project/netfilter-devel/list/?series=500684","date":"2026-04-20T21:11:04","name":"[nf,v2] netfilter: nft_compat: run checkentry() from .validate","version":2,"mbox":"http://patchwork.ozlabs.org/series/500684/mbox/"}],"comments":"http://patchwork.ozlabs.org/api/patches/2225338/comments/","check":"pending","checks":"http://patchwork.ozlabs.org/api/patches/2225338/checks/","tags":{},"headers":{"Return-Path":"\n <netfilter-devel+bounces-12093-incoming=patchwork.ozlabs.org@vger.kernel.org>","X-Original-To":["incoming@patchwork.ozlabs.org","netfilter-devel@vger.kernel.org"],"Delivered-To":"patchwork-incoming@legolas.ozlabs.org","Authentication-Results":["legolas.ozlabs.org;\n\tdkim=pass (2048-bit key;\n unprotected) header.d=netfilter.org header.i=@netfilter.org\n header.a=rsa-sha256 header.s=2025 header.b=Gu/QNdki;\n\tdkim-atps=neutral","legolas.ozlabs.org;\n spf=pass (sender SPF authorized) smtp.mailfrom=vger.kernel.org\n (client-ip=2600:3c04:e001:36c::12fc:5321; helo=tor.lore.kernel.org;\n envelope-from=netfilter-devel+bounces-12093-incoming=patchwork.ozlabs.org@vger.kernel.org;\n receiver=patchwork.ozlabs.org)","smtp.subspace.kernel.org;\n\tdkim=pass (2048-bit key) header.d=netfilter.org header.i=@netfilter.org\n header.b=\"Gu/QNdki\"","smtp.subspace.kernel.org;\n arc=none smtp.client-ip=217.70.190.124","smtp.subspace.kernel.org;\n dmarc=none (p=none dis=none) header.from=netfilter.org","smtp.subspace.kernel.org;\n spf=pass smtp.mailfrom=netfilter.org"],"Received":["from tor.lore.kernel.org (tor.lore.kernel.org\n [IPv6:2600:3c04:e001:36c::12fc:5321])\n\t(using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)\n\t key-exchange x25519 server-signature ECDSA (secp384r1) server-digest SHA384)\n\t(No client certificate requested)\n\tby legolas.ozlabs.org (Postfix) with ESMTPS id 4fzypL5Kt6z1yHB\n\tfor <incoming@patchwork.ozlabs.org>; Tue, 21 Apr 2026 07:11:22 +1000 (AEST)","from smtp.subspace.kernel.org (conduit.subspace.kernel.org\n [100.90.174.1])\n\tby tor.lore.kernel.org (Postfix) with ESMTP id 8D2A7301A161\n\tfor <incoming@patchwork.ozlabs.org>; Mon, 20 Apr 2026 21:11:19 +0000 (UTC)","from localhost.localdomain (localhost.localdomain [127.0.0.1])\n\tby smtp.subspace.kernel.org (Postfix) with ESMTP id B4BBD3A5E89;\n\tMon, 20 Apr 2026 21:11:18 +0000 (UTC)","from mail.netfilter.org (mail.netfilter.org [217.70.190.124])\n\t(using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits))\n\t(No client certificate requested)\n\tby smtp.subspace.kernel.org (Postfix) with ESMTPS id 70E0838836A\n\tfor <netfilter-devel@vger.kernel.org>; Mon, 20 Apr 2026 21:11:16 +0000 (UTC)","from localhost.localdomain (mail-agni [217.70.190.124])\n\tby mail.netfilter.org (Postfix) with ESMTPSA id DCFC960181\n\tfor <netfilter-devel@vger.kernel.org>; Mon, 20 Apr 2026 23:11:13 +0200 (CEST)"],"ARC-Seal":"i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116;\n\tt=1776719478; cv=none;\n b=HxTfLYmMXq0f7KHTQym2RaOA22JfuvRzu9DutpAe7vru+rXyoEDi/OqLjTCfPp8VQeV/q3UPoAmE6+/21ef2GupCR8cg8h3voSiHYcNV11D9W/wVy5GSd4CDFDss381VTiultjewIe9aXuCU8vhlySZxBz4B9AY6I3nc1PH6odk=","ARC-Message-Signature":"i=1; a=rsa-sha256; d=subspace.kernel.org;\n\ts=arc-20240116; t=1776719478; c=relaxed/simple;\n\tbh=tDNVRmC+8FUJaz3cjMfWgYLqy/u5Hf9J5F1oNtwnwt0=;\n\th=From:To:Subject:Date:Message-ID:MIME-Version;\n b=CBAVRTpq8FOn/Dby0nqtbIFkwRgtXA2/iq5sCXUJgvFUrBN3qIkeLexjIMMad0sRcVW6FiolmgQU7WVX6p4JXxPwakrDBCriYHEkb7wI8sI49EiOh6BEfRNcX+daJMYcfzcnp2gKqWS9n9HC5FS4kdjFFZ1akxsuOLCKMCKpwM8=","ARC-Authentication-Results":"i=1; smtp.subspace.kernel.org;\n dmarc=none (p=none dis=none) header.from=netfilter.org;\n spf=pass smtp.mailfrom=netfilter.org;\n dkim=pass (2048-bit key) header.d=netfilter.org header.i=@netfilter.org\n header.b=Gu/QNdki; arc=none smtp.client-ip=217.70.190.124","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/relaxed; d=netfilter.org;\n\ts=2025; t=1776719474;\n\tbh=CbJQsbTyQz0Pdj/+YEnzMEJr2U6zOThwT8P3ALasvMQ=;\n\th=From:To:Subject:Date:From;\n\tb=Gu/QNdkid+5eeIxcY75Ng1qLx9/j/aoWrmVJKEJpcvl9QIXHj5I3NAI6IGf06cP2P\n\t EzHYVOkL0wC7di6uvAb26W7KFQLeDS5w7ELt2donS1s8njSb4rKzFsoP5QsAe1ycXK\n\t 2XlWkFfWokFx/bFRtzPnA+pcykgdAxJ6PApMPb5N3ZpavO1PVgMSqrkfNidzfOfTRb\n\t g097l7Bp5LuCsRe5e0yfZXnVYHyVyHDrs33Hso12n841kX6yMNrKTMdSH9qvjyjG8f\n\t MWP4HBgdb1h5ibRCPWmvH/1q2CL8KVggwL6885qIky6dBZjXSnGjcJ3Z7K+HBc8pOX\n\t jL5fgso3PCVaw==","From":"Pablo Neira Ayuso <pablo@netfilter.org>","To":"netfilter-devel@vger.kernel.org","Subject":"[PATCH nf,v2] netfilter: nft_compat: run checkentry() from .validate","Date":"Mon, 20 Apr 2026 23:11:04 +0200","Message-ID":"<20260420211105.55177-1-pablo@netfilter.org>","X-Mailer":"git-send-email 2.47.3","Precedence":"bulk","X-Mailing-List":"netfilter-devel@vger.kernel.org","List-Id":"<netfilter-devel.vger.kernel.org>","List-Subscribe":"<mailto:netfilter-devel+subscribe@vger.kernel.org>","List-Unsubscribe":"<mailto:netfilter-devel+unsubscribe@vger.kernel.org>","MIME-Version":"1.0","Content-Transfer-Encoding":"8bit"},"content":"Several matches and one target check that the hook is correct from\ncheckentry(), however, the basechain is only available from\nnft_table_validate().\n\nThis patch calls checkentry() for matches and targets from the\nnft_compat expression .validate path for the following matches/target:\n\n- addrtype\n- devgroup\n- physdev\n- policy\n- set\n- TCPMSS\n- SET\n\nThe .destroy indirection is also called to restore the xt_set refcounts\nthat is performed by .checkentry. The remaining extensions provide no\n.destroy interface.\n\nThis patch sets the table in the nft_ctx struct in nft_table_validate()\nwhich is required by this patch.\n\nThe nft_compat_check_match() and nft_compat_check_target() helper\nfunctions are added to wrap common code used from .init and .validate\npath.\n\nThe protocol and inverse flags are set to always match from the\nexpression .validate path, this is already checked from the init path.\n\nFixes: 0ca743a55991 (\"netfilter: nf_tables: add compatibility layer for x_tables\")\nReported-by: Xiang Mei <xmei5@asu.edu>\nSigned-off-by: Pablo Neira Ayuso <pablo@netfilter.org>\n---\nv2: add SET target. Posting to the ML until it is decided where to go with this.\n\n net/netfilter/nf_tables_api.c |   1 +\n net/netfilter/nft_compat.c    | 152 +++++++++++++++++++++++++++-------\n 2 files changed, 125 insertions(+), 28 deletions(-)","diff":"diff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c\nindex d20ce5c36d31..38e33c66c618 100644\n--- a/net/netfilter/nf_tables_api.c\n+++ b/net/netfilter/nf_tables_api.c\n@@ -4205,6 +4205,7 @@ static int nft_table_validate(struct net *net, const struct nft_table *table)\n \tstruct nft_chain *chain;\n \tstruct nft_ctx ctx = {\n \t\t.net\t= net,\n+\t\t.table\t= (struct nft_table *)table,\n \t\t.family\t= table->family,\n \t};\n \tint err = 0;\ndiff --git a/net/netfilter/nft_compat.c b/net/netfilter/nft_compat.c\nindex decc725a33c2..5a5f4f07deab 100644\n--- a/net/netfilter/nft_compat.c\n+++ b/net/netfilter/nft_compat.c\n@@ -229,6 +229,20 @@ static int nft_parse_compat(const struct nlattr *attr, u16 *proto, bool *inv)\n \treturn 0;\n }\n \n+static int nft_compat_check_target(const struct nft_ctx *ctx,\n+\t\t\t\t   const struct nft_expr *expr,\n+\t\t\t\t   void *info, size_t size,\n+\t\t\t\t   u16 proto, bool inv)\n+{\n+\tstruct xt_target *target = expr->ops->data;\n+\tstruct xt_tgchk_param par;\n+\tunion nft_entry e = {};\n+\n+\tnft_target_set_tgchk_param(&par, ctx, target, info, &e, proto, inv);\n+\n+\treturn xt_check_target(&par, size, proto, inv);\n+}\n+\n static void nft_compat_wait_for_destructors(struct net *net)\n {\n \t/* xtables matches or targets can have side effects, e.g.\n@@ -244,13 +258,11 @@ static int\n nft_target_init(const struct nft_ctx *ctx, const struct nft_expr *expr,\n \t\tconst struct nlattr * const tb[])\n {\n-\tvoid *info = nft_expr_priv(expr);\n \tstruct xt_target *target = expr->ops->data;\n-\tstruct xt_tgchk_param par;\n \tsize_t size = XT_ALIGN(nla_len(tb[NFTA_TARGET_INFO]));\n-\tu16 proto = 0;\n+\tvoid *info = nft_expr_priv(expr);\n \tbool inv = false;\n-\tunion nft_entry e = {};\n+\tu16 proto = 0;\n \tint ret;\n \n \ttarget_compat_from_user(target, nla_data(tb[NFTA_TARGET_INFO]), info);\n@@ -261,11 +273,9 @@ nft_target_init(const struct nft_ctx *ctx, const struct nft_expr *expr,\n \t\t\treturn ret;\n \t}\n \n-\tnft_target_set_tgchk_param(&par, ctx, target, info, &e, proto, inv);\n-\n \tnft_compat_wait_for_destructors(ctx->net);\n \n-\tret = xt_check_target(&par, size, proto, inv);\n+\tret = nft_compat_check_target(ctx, expr, info, size, proto, inv);\n \tif (ret < 0) {\n \t\tif (ret == -ENOENT) {\n \t\t\tconst char *modname = NULL;\n@@ -296,21 +306,29 @@ static void __nft_mt_tg_destroy(struct module *me, const struct nft_expr *expr)\n \tkfree(expr->ops);\n }\n \n-static void\n-nft_target_destroy(const struct nft_ctx *ctx, const struct nft_expr *expr)\n+static void nft_tg_destroy(const struct nft_ctx *ctx, struct xt_target *target,\n+\t\t\t   void *info)\n {\n-\tstruct xt_target *target = expr->ops->data;\n-\tvoid *info = nft_expr_priv(expr);\n-\tstruct module *me = target->me;\n \tstruct xt_tgdtor_param par;\n \n+\tif (!target->destroy)\n+\t\treturn;\n+\n \tpar.net = ctx->net;\n \tpar.target = target;\n \tpar.targinfo = info;\n \tpar.family = ctx->family;\n-\tif (par.target->destroy != NULL)\n-\t\tpar.target->destroy(&par);\n+\tpar.target->destroy(&par);\n+}\n \n+static void\n+nft_target_destroy(const struct nft_ctx *ctx, const struct nft_expr *expr)\n+{\n+\tstruct xt_target *target = expr->ops->data;\n+\tvoid *info = nft_expr_priv(expr);\n+\tstruct module *me = target->me;\n+\n+\tnft_tg_destroy(ctx, target, info);\n \t__nft_mt_tg_destroy(me, expr);\n }\n \n@@ -382,6 +400,32 @@ static int nft_target_validate(const struct nft_ctx *ctx,\n \t\tif (target->hooks && !(hook_mask & target->hooks))\n \t\t\treturn -EINVAL;\n \n+\t\t/* At least one target needs to validate hooks at checkentry()\n+\t\t * stage because such validation depends on the match\n+\t\t * configuration. This cannot be enabled for all matches,\n+\t\t * because some of them perform more than simple validation,\n+\t\t * such as bumping reference counter on objects.\n+\t\t */\n+\t\tif (!strcmp(target->name, \"TCPMSS\") ||\n+\t\t    !strcmp(target->name, \"SET\")) {\n+\t\t\tstruct xt_target *target = expr->ops->data;\n+\t\t\tsize_t size = XT_ALIGN(target->targetsize);\n+\t\t\tvoid *info = nft_expr_priv(expr);\n+\n+\t\t\t/* nft_target_init() already checked for protocol and\n+\t\t\t * inverse, not available in this patch, lie here.\n+\t\t\t */\n+\t\t\tret = nft_compat_check_target(ctx, expr, info, size,\n+\t\t\t\t\t\t      target->proto, false);\n+\t\t\tif (ret < 0)\n+\t\t\t\treturn ret;\n+\n+\t\t\t /* The set target bumps reference count, restore after\n+\t\t\t  * this checkentry call.\n+\t\t\t  */\n+\t\t\tnft_tg_destroy(ctx, target, info);\n+\t\t}\n+\n \t\tret = nft_compat_chain_validate_dependency(ctx, target->table);\n \t\tif (ret < 0)\n \t\t\treturn ret;\n@@ -494,17 +538,29 @@ static void match_compat_from_user(struct xt_match *m, void *in, void *out)\n \t\tmemset(out + m->matchsize, 0, pad);\n }\n \n+static int nft_compat_check_match(const struct nft_ctx *ctx,\n+\t\t\t\t  const struct nft_expr *expr,\n+\t\t\t\t  void *info, size_t size,\n+\t\t\t\t  u16 proto, bool inv)\n+{\n+\tstruct xt_match *match = expr->ops->data;\n+\tstruct xt_mtchk_param par;\n+\tunion nft_entry e = {};\n+\n+\tnft_match_set_mtchk_param(&par, ctx, match, info, &e, proto, inv);\n+\n+\treturn xt_check_match(&par, size, proto, inv);\n+}\n+\n static int\n __nft_match_init(const struct nft_ctx *ctx, const struct nft_expr *expr,\n \t\t const struct nlattr * const tb[],\n \t\t void *info)\n {\n-\tstruct xt_match *match = expr->ops->data;\n-\tstruct xt_mtchk_param par;\n \tsize_t size = XT_ALIGN(nla_len(tb[NFTA_MATCH_INFO]));\n-\tu16 proto = 0;\n+\tstruct xt_match *match = expr->ops->data;\n \tbool inv = false;\n-\tunion nft_entry e = {};\n+\tu16 proto = 0;\n \tint ret;\n \n \tmatch_compat_from_user(match, nla_data(tb[NFTA_MATCH_INFO]), info);\n@@ -515,11 +571,9 @@ __nft_match_init(const struct nft_ctx *ctx, const struct nft_expr *expr,\n \t\t\treturn ret;\n \t}\n \n-\tnft_match_set_mtchk_param(&par, ctx, match, info, &e, proto, inv);\n-\n \tnft_compat_wait_for_destructors(ctx->net);\n \n-\treturn xt_check_match(&par, size, proto, inv);\n+\treturn nft_compat_check_match(ctx, expr, info, size, proto, inv);\n }\n \n static int\n@@ -547,21 +601,29 @@ nft_match_large_init(const struct nft_ctx *ctx, const struct nft_expr *expr,\n \treturn ret;\n }\n \n-static void\n-__nft_match_destroy(const struct nft_ctx *ctx, const struct nft_expr *expr,\n-\t\t    void *info)\n+static void nft_mt_destroy(const struct nft_ctx *ctx, struct xt_match *match,\n+\t\t\t   void *info)\n {\n-\tstruct xt_match *match = expr->ops->data;\n-\tstruct module *me = match->me;\n \tstruct xt_mtdtor_param par;\n \n+\tif (!match->destroy)\n+\t\treturn;\n+\n \tpar.net = ctx->net;\n \tpar.match = match;\n \tpar.matchinfo = info;\n \tpar.family = ctx->family;\n-\tif (par.match->destroy != NULL)\n-\t\tpar.match->destroy(&par);\n+\tpar.match->destroy(&par);\n+}\n \n+static void\n+__nft_match_destroy(const struct nft_ctx *ctx, const struct nft_expr *expr,\n+\t\t    void *info)\n+{\n+\tstruct xt_match *match = expr->ops->data;\n+\tstruct module *me = match->me;\n+\n+\tnft_mt_destroy(ctx, match, info);\n \t__nft_mt_tg_destroy(me, expr);\n }\n \n@@ -643,6 +705,40 @@ static int nft_match_validate(const struct nft_ctx *ctx,\n \t\tif (match->hooks && !(hook_mask & match->hooks))\n \t\t\treturn -EINVAL;\n \n+\t\t/* Several matches need to validate hooks at checkentry() stage\n+\t\t * because such validation depends on the match configuration.\n+\t\t */\n+\t\tif (!strcmp(match->name, \"addrtype\") ||\n+\t\t    !strcmp(match->name, \"devgroup\") ||\n+\t\t    !strcmp(match->name, \"physdev\") ||\n+\t\t    !strcmp(match->name, \"policy\") ||\n+\t\t    !strcmp(match->name, \"set\")) {\n+\t\t\tstruct xt_match *match = expr->ops->data;\n+\t\t\tsize_t size = XT_ALIGN(match->matchsize);\n+\t\t\tvoid *info;\n+\n+\t\t\tif (NFT_EXPR_SIZE(size) > NFT_MATCH_LARGE_THRESH) {\n+\t\t\t\tstruct nft_xt_match_priv *priv = nft_expr_priv(expr);\n+\n+\t\t\t\tinfo = priv->info;\n+\t\t\t} else {\n+\t\t\t\tinfo = nft_expr_priv(expr);\n+\t\t\t}\n+\n+\t\t\t/* __nft_match_init() already checked for protocol and\n+\t\t\t * inverse, not available in this patch, lie here.\n+\t\t\t */\n+\t\t\tret = nft_compat_check_match(ctx, expr, info, size,\n+\t\t\t\t\t\t     match->proto, false);\n+\t\t\tif (ret < 0)\n+\t\t\t\treturn ret;\n+\n+\t\t\t /* The set match bumps reference count, restore after\n+\t\t\t  * this checkentry call.\n+\t\t\t  */\n+\t\t\tnft_mt_destroy(ctx, match, info);\n+\t\t}\n+\n \t\tret = nft_compat_chain_validate_dependency(ctx, match->table);\n \t\tif (ret < 0)\n \t\t\treturn ret;\n","prefixes":["nf","v2"]}