From 6302f7dc2a9e561ff037de4d8b45bb596a45f5f3 Mon Sep 17 00:00:00 2001 From: eabdullin Date: Tue, 4 Nov 2025 10:28:18 +0000 Subject: [PATCH] import UBI nftables-1.0.9-4.el9_6 --- ...ropriately-with-multidevice-in-chain.patch | 213 +++++++++++++ ...n-fix-handle-memleak-from-error-path.patch | 280 ++++++++++++++++++ SPECS/nftables.spec | 8 +- 3 files changed, 500 insertions(+), 1 deletion(-) create mode 100644 SOURCES/0003-json-deal-appropriately-with-multidevice-in-chain.patch create mode 100644 SOURCES/0004-parser_json-fix-handle-memleak-from-error-path.patch diff --git a/SOURCES/0003-json-deal-appropriately-with-multidevice-in-chain.patch b/SOURCES/0003-json-deal-appropriately-with-multidevice-in-chain.patch new file mode 100644 index 0000000..75b7f71 --- /dev/null +++ b/SOURCES/0003-json-deal-appropriately-with-multidevice-in-chain.patch @@ -0,0 +1,213 @@ +From f85059245c5c5da7c0db98c3f35ab28a73e7cf4c Mon Sep 17 00:00:00 2001 +From: Phil Sutter +Date: Wed, 23 Apr 2025 14:25:43 +0200 +Subject: [PATCH] json: deal appropriately with multidevice in chain + +JIRA: https://issues.redhat.com/browse/RHEL-88181 +Upstream Status: nftables commit 4dfb5b2010917da3f9f11c83931069931a7d6fb3 +Conflicts: Dropped changes to shell testsuite stored dumps, downstream + misses commit 3d24b16b9ac13 ("tests/shell: add JSON dump + files"). + +commit 4dfb5b2010917da3f9f11c83931069931a7d6fb3 +Author: Pablo Neira Ayuso +Date: Thu Nov 23 10:36:50 2023 +0100 + + json: deal appropriately with multidevice in chain + + Chain device support is broken in JSON: listing does not include devices + and parser only deals with one single device. + + Use existing json_parse_flowtable_devs() function, rename it to + json_parse_devs() to parse the device array. + + Use the dev_array that contains the device names (as string) for the + listing. + + Update incorrect .json-nft files in tests/shell. + + Fixes: 3fdc7541fba0 ("src: add multidevice support for netdev chain") + Signed-off-by: Pablo Neira Ayuso + +Signed-off-by: Phil Sutter +--- + src/json.c | 25 ++++++------- + src/parser_json.c | 91 +++++++++++++++++++++++------------------------ + 2 files changed, 58 insertions(+), 58 deletions(-) + +diff --git a/src/json.c b/src/json.c +index 068c423..dad93e5 100644 +--- a/src/json.c ++++ b/src/json.c +@@ -257,9 +257,8 @@ static json_t *rule_print_json(struct output_ctx *octx, + + static json_t *chain_print_json(const struct chain *chain) + { +- int priority, policy, n = 0; +- struct expr *dev, *expr; +- json_t *root, *tmp; ++ json_t *root, *tmp, *devs = NULL; ++ int priority, policy, i; + + root = json_pack("{s:s, s:s, s:s, s:I}", + "family", family2str(chain->handle.family), +@@ -281,17 +280,19 @@ static json_t *chain_print_json(const struct chain *chain) + chain->hook.num), + "prio", priority, + "policy", chain_policy2str(policy)); +- if (chain->dev_expr) { +- list_for_each_entry(expr, &chain->dev_expr->expressions, list) { +- dev = expr; +- n++; +- } +- } + +- if (n == 1) { +- json_object_set_new(tmp, "dev", +- json_string(dev->identifier)); ++ for (i = 0; i < chain->dev_array_len; i++) { ++ const char *dev = chain->dev_array[i]; ++ if (!devs) ++ devs = json_string(dev); ++ else if (json_is_string(devs)) ++ devs = json_pack("[o, s]", devs, dev); ++ else ++ json_array_append_new(devs, json_string(dev)); + } ++ if (devs) ++ json_object_set_new(root, "dev", devs); ++ + json_object_update(root, tmp); + json_decref(tmp); + } +diff --git a/src/parser_json.c b/src/parser_json.c +index 9e5b656..d57aa6b 100644 +--- a/src/parser_json.c ++++ b/src/parser_json.c +@@ -3045,14 +3045,49 @@ static struct expr *parse_policy(const char *policy) + sizeof(int) * BITS_PER_BYTE, &policy_num); + } + ++static struct expr *json_parse_devs(struct json_ctx *ctx, json_t *root) ++{ ++ struct expr *tmp, *expr = compound_expr_alloc(int_loc, EXPR_LIST); ++ const char *dev; ++ json_t *value; ++ size_t index; ++ ++ if (!json_unpack(root, "s", &dev)) { ++ tmp = constant_expr_alloc(int_loc, &string_type, ++ BYTEORDER_HOST_ENDIAN, ++ strlen(dev) * BITS_PER_BYTE, dev); ++ compound_expr_add(expr, tmp); ++ return expr; ++ } ++ if (!json_is_array(root)) { ++ expr_free(expr); ++ return NULL; ++ } ++ ++ json_array_foreach(root, index, value) { ++ if (json_unpack(value, "s", &dev)) { ++ json_error(ctx, "Invalid device at index %zu.", ++ index); ++ expr_free(expr); ++ return NULL; ++ } ++ tmp = constant_expr_alloc(int_loc, &string_type, ++ BYTEORDER_HOST_ENDIAN, ++ strlen(dev) * BITS_PER_BYTE, dev); ++ compound_expr_add(expr, tmp); ++ } ++ return expr; ++} ++ + static struct cmd *json_parse_cmd_add_chain(struct json_ctx *ctx, json_t *root, + enum cmd_ops op, enum cmd_obj obj) + { + struct handle h = { + .table.location = *int_loc, + }; +- const char *family = "", *policy = "", *type, *hookstr, *name, *comment = NULL; ++ const char *family = "", *policy = "", *type, *hookstr, *comment = NULL; + struct chain *chain = NULL; ++ json_t *devs = NULL; + int prio; + + if (json_unpack_err(ctx, root, "{s:s, s:s}", +@@ -3107,16 +3142,15 @@ static struct cmd *json_parse_cmd_add_chain(struct json_ctx *ctx, json_t *root, + return NULL; + } + +- if (!json_unpack(root, "{s:s}", "dev", &name)) { +- struct expr *dev_expr, *expr; ++ json_unpack(root, "{s:o}", "dev", &devs); + +- dev_expr = compound_expr_alloc(int_loc, EXPR_LIST); +- expr = constant_expr_alloc(int_loc, &integer_type, +- BYTEORDER_HOST_ENDIAN, +- strlen(name) * BITS_PER_BYTE, +- name); +- compound_expr_add(dev_expr, expr); +- chain->dev_expr = dev_expr; ++ if (devs) { ++ chain->dev_expr = json_parse_devs(ctx, devs); ++ if (!chain->dev_expr) { ++ json_error(ctx, "Invalid chain dev."); ++ chain_free(chain); ++ return NULL; ++ } + } + + if (!json_unpack(root, "{s:s}", "policy", &policy)) { +@@ -3411,41 +3445,6 @@ static struct cmd *json_parse_cmd_add_element(struct json_ctx *ctx, + return cmd_alloc(op, cmd_obj, &h, int_loc, expr); + } + +-static struct expr *json_parse_flowtable_devs(struct json_ctx *ctx, +- json_t *root) +-{ +- struct expr *tmp, *expr = compound_expr_alloc(int_loc, EXPR_LIST); +- const char *dev; +- json_t *value; +- size_t index; +- +- if (!json_unpack(root, "s", &dev)) { +- tmp = constant_expr_alloc(int_loc, &string_type, +- BYTEORDER_HOST_ENDIAN, +- strlen(dev) * BITS_PER_BYTE, dev); +- compound_expr_add(expr, tmp); +- return expr; +- } +- if (!json_is_array(root)) { +- expr_free(expr); +- return NULL; +- } +- +- json_array_foreach(root, index, value) { +- if (json_unpack(value, "s", &dev)) { +- json_error(ctx, "Invalid flowtable dev at index %zu.", +- index); +- expr_free(expr); +- return NULL; +- } +- tmp = constant_expr_alloc(int_loc, &string_type, +- BYTEORDER_HOST_ENDIAN, +- strlen(dev) * BITS_PER_BYTE, dev); +- compound_expr_add(expr, tmp); +- } +- return expr; +-} +- + static struct cmd *json_parse_cmd_add_flowtable(struct json_ctx *ctx, + json_t *root, enum cmd_ops op, + enum cmd_obj cmd_obj) +@@ -3506,7 +3505,7 @@ static struct cmd *json_parse_cmd_add_flowtable(struct json_ctx *ctx, + sizeof(int) * BITS_PER_BYTE, &prio); + + if (devs) { +- flowtable->dev_expr = json_parse_flowtable_devs(ctx, devs); ++ flowtable->dev_expr = json_parse_devs(ctx, devs); + if (!flowtable->dev_expr) { + json_error(ctx, "Invalid flowtable dev."); + flowtable_free(flowtable); diff --git a/SOURCES/0004-parser_json-fix-handle-memleak-from-error-path.patch b/SOURCES/0004-parser_json-fix-handle-memleak-from-error-path.patch new file mode 100644 index 0000000..fc2b7ee --- /dev/null +++ b/SOURCES/0004-parser_json-fix-handle-memleak-from-error-path.patch @@ -0,0 +1,280 @@ +From c7225a21a31131edd16f018a4e4947944db88f01 Mon Sep 17 00:00:00 2001 +From: Phil Sutter +Date: Wed, 23 Apr 2025 14:26:47 +0200 +Subject: [PATCH] parser_json: fix handle memleak from error path + +JIRA: https://issues.redhat.com/browse/RHEL-88181 +Upstream Status: nftables commit 47e18c0eba51a538e1110322d1a9248b0501d7c8 + +commit 47e18c0eba51a538e1110322d1a9248b0501d7c8 +Author: Pablo Neira Ayuso +Date: Mon Aug 19 21:34:49 2024 +0200 + + parser_json: fix handle memleak from error path + + Based on patch from Sebastian Walz. + + Fixes: 586ad210368b ("libnftables: Implement JSON parser") + Signed-off-by: Pablo Neira Ayuso + +Signed-off-by: Phil Sutter +--- + src/parser_json.c | 93 ++++++++++++++++++++++++----------------------- + 1 file changed, 47 insertions(+), 46 deletions(-) + +diff --git a/src/parser_json.c b/src/parser_json.c +index d57aa6b..2acc248 100644 +--- a/src/parser_json.c ++++ b/src/parser_json.c +@@ -3138,8 +3138,7 @@ static struct cmd *json_parse_cmd_add_chain(struct json_ctx *ctx, json_t *root, + chain->hook.name = chain_hookname_lookup(hookstr); + if (!chain->hook.name) { + json_error(ctx, "Invalid chain hook '%s'.", hookstr); +- chain_free(chain); +- return NULL; ++ goto err_free_chain; + } + + json_unpack(root, "{s:o}", "dev", &devs); +@@ -3148,8 +3147,7 @@ static struct cmd *json_parse_cmd_add_chain(struct json_ctx *ctx, json_t *root, + chain->dev_expr = json_parse_devs(ctx, devs); + if (!chain->dev_expr) { + json_error(ctx, "Invalid chain dev."); +- chain_free(chain); +- return NULL; ++ goto err_free_chain; + } + } + +@@ -3157,8 +3155,7 @@ static struct cmd *json_parse_cmd_add_chain(struct json_ctx *ctx, json_t *root, + chain->policy = parse_policy(policy); + if (!chain->policy) { + json_error(ctx, "Unknown policy '%s'.", policy); +- chain_free(chain); +- return NULL; ++ goto err_free_chain; + } + } + +@@ -3167,6 +3164,11 @@ static struct cmd *json_parse_cmd_add_chain(struct json_ctx *ctx, json_t *root, + + handle_merge(&chain->handle, &h); + return cmd_alloc(op, obj, &h, int_loc, chain); ++ ++err_free_chain: ++ chain_free(chain); ++ handle_free(&h); ++ return NULL; + } + + static struct cmd *json_parse_cmd_add_rule(struct json_ctx *ctx, json_t *root, +@@ -3206,6 +3208,7 @@ static struct cmd *json_parse_cmd_add_rule(struct json_ctx *ctx, json_t *root, + + if (!json_is_array(tmp)) { + json_error(ctx, "Value of property \"expr\" must be an array."); ++ handle_free(&h); + return NULL; + } + +@@ -3225,16 +3228,14 @@ static struct cmd *json_parse_cmd_add_rule(struct json_ctx *ctx, json_t *root, + if (!json_is_object(value)) { + json_error(ctx, "Unexpected expr array element of type %s, expected object.", + json_typename(value)); +- rule_free(rule); +- return NULL; ++ goto err_free_rule; + } + + stmt = json_parse_stmt(ctx, value); + + if (!stmt) { + json_error(ctx, "Parsing expr array at index %zd failed.", index); +- rule_free(rule); +- return NULL; ++ goto err_free_rule; + } + + rule_stmt_append(rule, stmt); +@@ -3244,6 +3245,11 @@ static struct cmd *json_parse_cmd_add_rule(struct json_ctx *ctx, json_t *root, + json_object_del(root, "handle"); + + return cmd_alloc(op, obj, &h, int_loc, rule); ++ ++err_free_rule: ++ rule_free(rule); ++ handle_free(&h); ++ return NULL; + } + + static int string_to_nft_object(const char *str) +@@ -3617,8 +3623,7 @@ static struct cmd *json_parse_cmd_add_object(struct json_ctx *ctx, + if (ret < 0 || ret >= (int)sizeof(obj->secmark.ctx)) { + json_error(ctx, "Invalid secmark context '%s', max length is %zu.", + tmp, sizeof(obj->secmark.ctx)); +- obj_free(obj); +- return NULL; ++ goto err_free_obj; + } + } + break; +@@ -3634,8 +3639,7 @@ static struct cmd *json_parse_cmd_add_object(struct json_ctx *ctx, + ret >= (int)sizeof(obj->ct_helper.name)) { + json_error(ctx, "Invalid CT helper type '%s', max length is %zu.", + tmp, sizeof(obj->ct_helper.name)); +- obj_free(obj); +- return NULL; ++ goto err_free_obj; + } + } + if (!json_unpack(root, "{s:s}", "protocol", &tmp)) { +@@ -3645,15 +3649,13 @@ static struct cmd *json_parse_cmd_add_object(struct json_ctx *ctx, + obj->ct_helper.l4proto = IPPROTO_UDP; + } else { + json_error(ctx, "Invalid ct helper protocol '%s'.", tmp); +- obj_free(obj); +- return NULL; ++ goto err_free_obj; + } + } + if (!json_unpack(root, "{s:s}", "l3proto", &tmp) && + parse_family(tmp, &l3proto)) { + json_error(ctx, "Invalid ct helper l3proto '%s'.", tmp); +- obj_free(obj); +- return NULL; ++ goto err_free_obj; + } + obj->ct_helper.l3proto = l3proto; + break; +@@ -3667,23 +3669,19 @@ static struct cmd *json_parse_cmd_add_object(struct json_ctx *ctx, + obj->ct_timeout.l4proto = IPPROTO_UDP; + } else { + json_error(ctx, "Invalid ct timeout protocol '%s'.", tmp); +- obj_free(obj); +- return NULL; ++ goto err_free_obj; + } + } + if (!json_unpack(root, "{s:s}", "l3proto", &tmp) && + parse_family(tmp, &l3proto)) { + json_error(ctx, "Invalid ct timeout l3proto '%s'.", tmp); +- obj_free(obj); +- return NULL; ++ goto err_free_obj; + } + obj->ct_timeout.l3proto = l3proto; + + init_list_head(&obj->ct_timeout.timeout_list); +- if (json_parse_ct_timeout_policy(ctx, root, obj)) { +- obj_free(obj); +- return NULL; +- } ++ if (json_parse_ct_timeout_policy(ctx, root, obj)) ++ goto err_free_obj; + break; + case NFT_OBJECT_CT_EXPECT: + cmd_obj = CMD_OBJ_CT_EXPECT; +@@ -3691,8 +3689,7 @@ static struct cmd *json_parse_cmd_add_object(struct json_ctx *ctx, + if (!json_unpack(root, "{s:s}", "l3proto", &tmp) && + parse_family(tmp, &l3proto)) { + json_error(ctx, "Invalid ct expectation l3proto '%s'.", tmp); +- obj_free(obj); +- return NULL; ++ goto err_free_obj; + } + obj->ct_expect.l3proto = l3proto; + if (!json_unpack(root, "{s:s}", "protocol", &tmp)) { +@@ -3702,8 +3699,7 @@ static struct cmd *json_parse_cmd_add_object(struct json_ctx *ctx, + obj->ct_expect.l4proto = IPPROTO_UDP; + } else { + json_error(ctx, "Invalid ct expectation protocol '%s'.", tmp); +- obj_free(obj); +- return NULL; ++ goto err_free_obj; + } + } + if (!json_unpack(root, "{s:i}", "dport", &i)) +@@ -3717,10 +3713,9 @@ static struct cmd *json_parse_cmd_add_object(struct json_ctx *ctx, + obj->type = NFT_OBJECT_LIMIT; + if (json_unpack_err(ctx, root, "{s:I, s:s}", + "rate", &obj->limit.rate, +- "per", &tmp)) { +- obj_free(obj); +- return NULL; +- } ++ "per", &tmp)) ++ goto err_free_obj; ++ + json_unpack(root, "{s:s}", "rate_unit", &rate_unit); + json_unpack(root, "{s:b}", "inv", &inv); + json_unpack(root, "{s:i}", "burst", &obj->limit.burst); +@@ -3741,20 +3736,18 @@ static struct cmd *json_parse_cmd_add_object(struct json_ctx *ctx, + case CMD_OBJ_SYNPROXY: + obj->type = NFT_OBJECT_SYNPROXY; + if (json_unpack_err(ctx, root, "{s:i, s:i}", +- "mss", &i, "wscale", &j)) { +- obj_free(obj); +- return NULL; +- } ++ "mss", &i, "wscale", &j)) ++ goto err_free_obj; ++ + obj->synproxy.mss = i; + obj->synproxy.wscale = j; + obj->synproxy.flags |= NF_SYNPROXY_OPT_MSS; + obj->synproxy.flags |= NF_SYNPROXY_OPT_WSCALE; + if (!json_unpack(root, "{s:o}", "flags", &jflags)) { + flags = json_parse_synproxy_flags(ctx, jflags); +- if (flags < 0) { +- obj_free(obj); +- return NULL; +- } ++ if (flags < 0) ++ goto err_free_obj; ++ + obj->synproxy.flags |= flags; + } + break; +@@ -3766,6 +3759,11 @@ static struct cmd *json_parse_cmd_add_object(struct json_ctx *ctx, + json_object_del(root, "handle"); + + return cmd_alloc(op, cmd_obj, &h, int_loc, obj); ++ ++err_free_obj: ++ obj_free(obj); ++ handle_free(&h); ++ return NULL; + } + + static struct cmd *json_parse_cmd_add(struct json_ctx *ctx, +@@ -3879,8 +3877,7 @@ static struct cmd *json_parse_cmd_replace(struct json_ctx *ctx, + if (!json_is_object(value)) { + json_error(ctx, "Unexpected expr array element of type %s, expected object.", + json_typename(value)); +- rule_free(rule); +- return NULL; ++ goto err_free_replace; + } + + stmt = json_parse_stmt(ctx, value); +@@ -3888,8 +3885,7 @@ static struct cmd *json_parse_cmd_replace(struct json_ctx *ctx, + if (!stmt) { + json_error(ctx, "Parsing expr array at index %zd failed.", + index); +- rule_free(rule); +- return NULL; ++ goto err_free_replace; + } + + rule_stmt_append(rule, stmt); +@@ -3899,6 +3895,11 @@ static struct cmd *json_parse_cmd_replace(struct json_ctx *ctx, + json_object_del(root, "handle"); + + return cmd_alloc(op, CMD_OBJ_RULE, &h, int_loc, rule); ++ ++err_free_replace: ++ rule_free(rule); ++ handle_free(&h); ++ return NULL; + } + + static struct cmd *json_parse_cmd_list_multiple(struct json_ctx *ctx, diff --git a/SPECS/nftables.spec b/SPECS/nftables.spec index e31cf82..ce0b8d6 100644 --- a/SPECS/nftables.spec +++ b/SPECS/nftables.spec @@ -1,5 +1,5 @@ %define nft_rpmversion 1.0.9 -%define nft_specrelease 3 +%define nft_specrelease 4 Name: nftables Version: %{nft_rpmversion} @@ -21,6 +21,8 @@ Source7: run-tests.stderr.expect Patch1: 0001-Add-support-for-table-s-persist-flag.patch Patch2: 0002-cache-Always-set-NFT_CACHE_TERSE-for-list-cmd-with-t.patch +Patch3: 0003-json-deal-appropriately-with-multidevice-in-chain.patch +Patch4: 0004-parser_json-fix-handle-memleak-from-error-path.patch BuildRequires: autoconf BuildRequires: automake @@ -133,6 +135,10 @@ cd py/ %files -n python3-nftables -f %{pyproject_files} %changelog +* Wed Apr 23 2025 Phil Sutter [1.0.9-4.el9] +- parser_json: fix handle memleak from error path (Phil Sutter) [RHEL-88181] +- json: deal appropriately with multidevice in chain (Phil Sutter) [RHEL-88181] + * Tue Jul 02 2024 Phil Sutter [1.0.9-3.el9] - cache: Always set NFT_CACHE_TERSE for list cmd with --terse (Phil Sutter) [RHEL-45633]