]> git.ipfire.org Git - thirdparty/nftables.git/commitdiff
segtree: Fix range aggregation on Big Endian
authorPhil Sutter <phil@nwl.cc>
Tue, 21 Oct 2025 16:36:10 +0000 (18:36 +0200)
committerPhil Sutter <phil@nwl.cc>
Tue, 27 Jan 2026 22:01:54 +0000 (23:01 +0100)
Interface name wildcards are received as ranges from prefix with zero
padding to prefix with ones padding. E.g. with "abcd*" (in hex):

61626364000000000000000000000000 - 61626364ffffffffffffffffffffffff

The faulty code tries to export the prefix from the lower boundary (r1)
into a buffer, append "*" and allocate a constant expression from the
resulting string. This does not work on Big Endian though:
mpz_export_data() seems to zero-pad data upon export and not necessarily
respect the passed length value. Moreover, this padding appears in the
first bytes of the buffer. The amount of padding seems illogical, too:
While a 6B prefix causes 2B padding and 8B prefix no padding, 10B prefix
causes 4B padding and 12B prefix even 8B padding.

Work around the odd behaviour by exporting the full data into a larger
buffer.

A similar issue is caused by increasing the constant expression's length
to match the upper boundary data length: Data export when printing puts
the padding upfront, so the resulting string starts with NUL-chars.
Since this length adjustment seems not to have any effect in practice,
just drop it.

Fixes: 88b2345a215ef ("segtree: add pretty-print support for wildcard strings in concatenated sets")
Signed-off-by: Phil Sutter <phil@nwl.cc>
src/segtree.c

index cf2b4f129096f2a5f3f6038f813feb7e9e2393ca..b9d6891e4b8f6ba01c526e94e80007416d3a9e5b 100644 (file)
@@ -409,16 +409,16 @@ void concat_range_aggregate(struct expr *set)
                        if (prefix_len >= 0 &&
                            (prefix_len % BITS_PER_BYTE) == 0 &&
                            string_type) {
+                               unsigned int r1len = div_round_up(r1->len, BITS_PER_BYTE);
                                unsigned int str_len = prefix_len / BITS_PER_BYTE;
-                               char data[str_len + 2];
+                               char data[r1len + 1] = {};
 
-                               mpz_export_data(data, r1->value, BYTEORDER_HOST_ENDIAN, str_len);
+                               mpz_export_data(data, r1->value, BYTEORDER_HOST_ENDIAN, r1len);
                                data[str_len] = '*';
 
                                tmp = constant_expr_alloc(&r1->location, r1->dtype,
                                                          BYTEORDER_HOST_ENDIAN,
                                                          (str_len + 1) * BITS_PER_BYTE, data);
-                               tmp->len = r2->len;
                                list_replace(&r2->list, &tmp->list);
                                r2_next = tmp->list.next;
                                expr_free(r2);