On 2020-08-16, at 14:48:12 +0200, Bernhard Übelacker wrote:
> Program received signal SIGSEGV, Segmentation fault.
> nftnl_rule_list_add (r=r@entry=0x5555555f5900, list=0x0) at rule.c:782
> 782             list_add(&r->head, &list->list);
> (gdb) bt
> #0  nftnl_rule_list_add (r=r@entry=0x5555555f5900, list=0x0) at rule.c:782
> #1  0x0000555555567eac in nft_rule_insert (h=h@entry=0x7fffffffe480, 
> chain=chain@entry=0x7fffffffe848 "OUTPUT", table=table@entry=0x55555557b126 
> "filter", data=data@entry=0x7fffffffe300, rulenum=rulenum@entry=0, 
> verbose=verbose@entry=false) at nft.c:2146
> #2  0x0000555555562629 in add_entry (chain=0x7fffffffe848 "OUTPUT", 
> table=0x55555557b126 "filter", cs=cs@entry=0x7fffffffe300, rulenum=0, 
> family=2, s=..., d=..., verbose=false, h=0x7fffffffe480, append=false) at 
> xtables.c:412
> #3  0x0000555555564270 in do_commandx (h=h@entry=0x7fffffffe480, 
> argc=argc@entry=3, argv=argv@entry=0x7fffffffe608, 
> table=table@entry=0x7fffffffe478, restore=restore@entry=false) at 
> xtables.c:1122
> #4  0x0000555555562350 in xtables_main (family=family@entry=2, 
> progname=progname@entry=0x55555557a011 "iptables", argc=3, 
> argv=0x7fffffffe608) at xtables-standalone.c:72
> #5  0x000055555556248a in xtables_ip4_main (argc=<optimized out>, 
> argv=<optimized out>) at xtables-standalone.c:96
> #6  0x00007ffff763809b in __libc_start_main (main=0x55555555cfb0 <main>, 
> argc=3, argv=0x7fffffffe608, init=<optimized out>, fini=<optimized out>, 
> rtld_fini=<optimized out>, stack_end=0x7fffffffe5f8) at 
> ../csu/libc-start.c:308
> #7  0x000055555555cfea in _start ()

Here is the relevant part of nft_rule_insert (iptables/nft.c,
ll. 2131ff.):

        if (rulenum > 0) {
                list = nft_rule_list_get(h);
                if (list == NULL)
                        goto err;

                r = nft_rule_find(h, list, chain, table, data, rulenum);
                if (r == NULL) {
                        /* special case: iptables allows to insert into
                         * rule_count + 1 position.
                         */
                        r = nft_rule_find(h, list, chain, table, data,
                                          rulenum - 1);
                        if (r != NULL)
                                return nft_rule_append(h, chain, table, data,
                                                       0, verbose);

                        errno = ENOENT;
                        goto err;
                }

                handle = nftnl_rule_get_u64(r, NFTNL_RULE_HANDLE);
                DEBUGP("adding after rule handle %"PRIu64"\n", handle);
        } else {
                nft_rule_list_get(h);
        }

        new_rule = nft_rule_add(h, chain, table, data, handle, verbose);
        if (!new_rule)
                goto err;

        if (handle)
                nftnl_rule_list_insert_at(new_rule, r);
        else
                nftnl_rule_list_add(new_rule, h->rule_cache);

If rulenum == 0, the code assumes that nft_rule_list_get, which sets
h->rule_cache, does not fail.

Here is the relevant part of nft_rule_list_get (iptables/nft.c,
ll. 1407ff.):

        nlh = nftnl_rule_nlmsg_build_hdr(buf, NFT_MSG_GETRULE, h->family,
                                        NLM_F_DUMP, h->seq);

        ret = mnl_talk(h, nlh, nftnl_rule_list_cb, list);
        if (ret < 0) {
                if (errno == EINTR) {
                        assert(nft_restart(h) >= 0);
                        nftnl_rule_list_free(list);
                        goto retry;
                }

                nftnl_rule_list_free(list);
                return NULL;
        }

Because we are running iptables as an unprivileged user, the kernel
returns EPERM in response to our NFT_MSG_GETRULE request.

We need to check the return value of nft_rule_list_get in both cases.

Patch attached.

J.
--- iptables/nft.c.orig	2020-08-16 15:58:02.799901382 +0100
+++ iptables/nft.c	2020-08-16 15:58:07.507901423 +0100
@@ -2110,11 +2110,11 @@
 
 	nft_fn = nft_rule_insert;
 
-	if (rulenum > 0) {
-		list = nft_rule_list_get(h);
-		if (list == NULL)
-			goto err;
+	list = nft_rule_list_get(h);
+	if (list == NULL)
+		goto err;
 
+	if (rulenum > 0) {
 		r = nft_rule_find(h, list, chain, table, data, rulenum);
 		if (r == NULL) {
 			/* special case: iptables allows to insert into
@@ -2132,8 +2132,6 @@
 
 		handle = nftnl_rule_get_u64(r, NFTNL_RULE_HANDLE);
 		DEBUGP("adding after rule handle %"PRIu64"\n", handle);
-	} else {
-		nft_rule_list_get(h);
 	}
 
 	new_rule = nft_rule_add(h, chain, table, data, handle, verbose);

Attachment: signature.asc
Description: PGP signature

Reply via email to