[PATCH] iptables: Fix crash with iptables >= 1.8.11

LiangCheng Wang <[email protected]> Wed, 8 Jul 2026 11:39:26 +0800
Newsgroups dev.linux.lists.connman
Message-ID <[email protected]>
Since iptables 1.8.11, xtables_merge_options() calls
xtables_free_opts(), which unconditionally free()s xt_params->opts.
ConnMan points opts at the static iptables_opts[] array, so the first
option merge for a target or match with extra options (e.g. the
MASQUERADE target when enabling tethering) aborts the daemon:

  free(): invalid pointer
  connmand[1223]: Aborting (signal 6) [/usr/sbin/connmand]

with the invalid free happening inside libxtables.so.12 called from
prepare_target().

Fix this by keeping xt_params->opts as a heap-allocated copy of
orig_opts at all times, so that libxtables is free to release and
replace it. This stays compatible with older iptables versions, where
xtables_free_opts() only frees opts when it differs from orig_opts.

Reproduced with ConnMan 1.43 and iptables 1.8.11 by enabling WiFi
tethering. Same issue reported in Debian bug #1112242 against
ConnMan 1.45 and Bluetooth tethering.

Signed-off-by: LiangCheng Wang <[email protected]>
---
 src/iptables.c | 29 ++++++++++++++++++++++++++---
 1 file changed, 26 insertions(+), 3 deletions(-)

diff --git a/src/iptables.c b/src/iptables.c
index a81a0779..956a5b6b 100644
--- a/src/iptables.c
+++ b/src/iptables.c
@@ -3541,6 +3541,23 @@ static int setup_xtables(int type)
 	return err;
 }
 
+static struct option *dup_orig_opts(const struct option *orig_opts)
+{
+	struct option *opts;
+	unsigned int i;
+
+	for (i = 0; orig_opts[i].name; i++)
+		;
+
+	opts = g_try_malloc0(sizeof(struct option) * (i + 1));
+	if (!opts)
+		return NULL;
+
+	memcpy(opts, orig_opts, sizeof(struct option) * i);
+
+	return opts;
+}
+
 static void reset_xtables(void)
 {
 	struct xtables_match *xt_m;
@@ -3566,11 +3583,17 @@ static void reset_xtables(void)
 	 * We need also to free the memory implicitly allocated
 	 * during parsing (see xtables_options_xfrm()).
 	 * Note xt_params is actually iptables_globals.
+	 *
+	 * Since iptables 1.8.11 xtables_merge_options() calls
+	 * xtables_free_opts(), which unconditionally free()s
+	 * xt_params->opts. Therefore opts must never point at the
+	 * static orig_opts array; keep it as a heap-allocated copy
+	 * that libxtables is free to release.
 	 */
-	if (xt_params->opts != xt_params->orig_opts) {
+	if (xt_params->opts != xt_params->orig_opts)
 		g_free(xt_params->opts);
-		xt_params->opts = xt_params->orig_opts;
-	}
+
+	xt_params->opts = dup_orig_opts(xt_params->orig_opts);
 	xt_params->option_offset = 0;
 }
 
-- 
2.34.1