[PATCH nft] parser_json: initialize geneve options list for empty tunnel array

Omkhar Arasaratnam <[email protected]>
Newsgroups gmane.comp.security.firewalls.netfilter.devel
Message-ID <LV0SPRMB0026D6A74A8E3E8504609CB0A1FF2@LV0SPRMB0026.namprd21.prod.outlook.com>
Hi,

The attached patch fixes a crash in the JSON parser. A geneve tunnel object with an empty options array ("tunnel": []) leaves obj->tunnel.geneve_opts uninitialized; obj_tunnel_add_opts() then walks the uninitialized list head and nft -j crashes (SEGV in nftnl_tunnel_opt_geneve_set via near-NULL deref).

The equivalent native-syntax empty definition was already handled in f9047c1f ("evaluate: tunnel: don't assume src is set"); this covers the JSON parser path, which that fix did not reach. It is independent of Phil Sutter's pending "parser_json: Introduce json_parse_tunnel()" refactor, which relocates this block but carries the "if (index == 0)" guard forward unchanged -- happy to rebase onto or fold into that series if you prefer.

I'm sending the patch as an attachment because my mail client mangles inline patches; it applies cleanly with git am. The full commit message, crash trace, reproducer, and diff are in the attachment.

— oa
nftables-parse-0001.patch (application/octet-stream, 3.1 KB)
From de817ec180a37cc9af570809b632df984374fba4 Mon Sep 17 00:00:00 2001
From: Omkhar Arasaratnam <[email protected]>
Date: Wed, 8 Jul 2026 23:09:52 +0000
Subject: [PATCH nft] parser_json: initialize geneve options list for empty
 tunnel array

json_parse_cmd_add_object() only initializes obj->tunnel.geneve_opts on
the first iteration of the json_array_foreach() loop, guarded by
"if (index == 0)". A geneve tunnel object whose options array is empty
("tunnel": []) never enters the loop, so the list head is left
uninitialized. obj_tunnel_add_opts() (src/mnl.c) later walks it with
list_for_each_entry() and passes the bogus element to libnftnl, which
dereferences the near-NULL pointer and crashes nft:

  # nft -j -f empty_geneve.json
  AddressSanitizer: SEGV on unknown address 0x000000000012
    #1 nftnl_tunnel_opt_geneve_set  obj/tunnel.c:880
    #2 nftnl_tunnel_opt_set         obj/tunnel.c:910
    #3 obj_tunnel_add_opts          src/mnl.c:1608
    #4 mnl_nft_obj_add              src/mnl.c:1757
    #5 do_command_add               src/rule.c:1542
    #6 do_command                   src/rule.c:2802
    #10 main                        src/main.c:539

where empty_geneve.json is:

  { "nftables": [
    { "add": { "table": { "family": "netdev", "name": "x" } } },
    { "add": { "tunnel": { "family": "netdev", "name": "t", "table": "x",
        "src-ipv4": "192.168.2.10", "dst-ipv4": "192.168.2.11",
        "type": "geneve", "tunnel": [] } } } ] }

Initialize the list head unconditionally before the loop and drop the
per-iteration guard, so an empty array leaves a valid empty list. The
equivalent native-syntax empty definition was already handled in commit
f9047c1f ("evaluate: tunnel: don't assume src is set"); this covers the
JSON parser path, which that fix did not reach.

Fixes: 3a957f8f1ff1 ("tunnel: add tunnel object and statement json support")
Signed-off-by: Omkhar Arasaratnam <[email protected]>
---
Note: independent of Phil Sutter's pending series "Eliminate variable
declarations in switch cases" (nft PATCH 0/6, 2026-06-03), whose 2/6
"parser_json: Introduce json_parse_tunnel()" relocates this block but carries
the "if (index == 0)" guard forward unchanged. This is a standalone,
backportable crash fix; happy to rebase on top of that series or fold it in.
 src/parser_json.c | 5 ++---
 1 file changed, 2 insertions(+), 3 deletions(-)

diff --git a/src/parser_json.c b/src/parser_json.c
index f04772a0..6a0c1745 100644
--- a/src/parser_json.c
+++ b/src/parser_json.c
@@ -3941,6 +3941,8 @@ static struct cmd *json_parse_cmd_add_object(struct json_ctx *ctx,
 					    "{s:o}", "tunnel", &tmp_json))
 				goto err_free_obj;
 
+			init_list_head(&obj->tunnel.geneve_opts);
+
 			json_array_foreach(tmp_json, index, value) {
 				struct tunnel_geneve *geneve = xmalloc(sizeof(struct tunnel_geneve));
 				if (!geneve)
@@ -3963,9 +3965,6 @@ static struct cmd *json_parse_cmd_add_object(struct json_ctx *ctx,
 					goto err_free_obj;
 				}
 
-				if (index == 0)
-					init_list_head(&obj->tunnel.geneve_opts);
-
 				list_add_tail(&geneve->list, &obj->tunnel.geneve_opts);
 			}
 			break;
-- 
2.43.0
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.