]> git.ipfire.org Git - thirdparty/iptables.git/commitdiff
ebtables: Clone extensions before modifying them
authorPhil Sutter <phil@nwl.cc>
Thu, 31 Oct 2024 15:18:13 +0000 (16:18 +0100)
committerPhil Sutter <phil@nwl.cc>
Tue, 5 Nov 2024 22:58:03 +0000 (23:58 +0100)
Upon identifying an extension option, ebt_command_default() would have
the extension parse the option prior to creating a copy for attaching to
the iptables_command_state object. After copying, the (modified)
initial extension's data was cleared.

This somewhat awkward process breaks with among match which increases
match_size if needed (but never reduces it). This change is not undone,
hence leaks into following instances. This in turn is problematic with
ebtables-restore only (as multiple rules are parsed) and specifically
when deleting rules as the potentially over-sized match_size won't match
the one parsed from the kernel.

A workaround would be to make bramong_parse() realloc the match also if
new size is smaller than the old one. This patch attempts a proper fix
though, by making ebt_command_default() copy the extension first and
parsing the option into the copy afterwards.

No Fixes tag: Prior to commit 24bb57d3f52ac ("ebtables: Support for
guided option parser"), ebtables relied upon the extension's parser
return code instead of checking option_offset, so copying the extension
opportunistically wasn't feasible.

Signed-off-by: Phil Sutter <phil@nwl.cc>
iptables/nft-bridge.h
iptables/xtables-eb.c

index 13b077fc4fbf33036afca6c2a124957ee0ce3acb..54b473ebff6b03ece76a35ce167a8ac910a8901b 100644 (file)
@@ -108,10 +108,10 @@ static inline const char *ebt_target_name(unsigned int verdict)
 })                                                             \
 
 void ebt_cs_clean(struct iptables_command_state *cs);
-void ebt_add_match(struct xtables_match *m,
-                         struct iptables_command_state *cs);
-void ebt_add_watcher(struct xtables_target *watcher,
-                     struct iptables_command_state *cs);
+struct xtables_match *ebt_add_match(struct xtables_match *m,
+                                   struct iptables_command_state *cs);
+struct xtables_target *ebt_add_watcher(struct xtables_target *watcher,
+                                      struct iptables_command_state *cs);
 int ebt_command_default(struct iptables_command_state *cs,
                        struct xtables_globals *unused, bool ebt_invert);
 
index 658cf4b98c04dcc92ae307cbe2a677b8d29e71ae..06386cd90830cb2190fbfafbd3adf603b5b955a6 100644 (file)
@@ -367,8 +367,8 @@ static void ebt_load_match_extensions(void)
        ebt_load_watcher("nflog");
 }
 
-void ebt_add_match(struct xtables_match *m,
-                  struct iptables_command_state *cs)
+struct xtables_match *ebt_add_match(struct xtables_match *m,
+                                   struct iptables_command_state *cs)
 {
        struct xtables_rule_match **rule_matches = &cs->matches;
        struct xtables_match *newm;
@@ -397,10 +397,12 @@ void ebt_add_match(struct xtables_match *m,
        for (matchp = &cs->match_list; *matchp; matchp = &(*matchp)->next)
                ;
        *matchp = newnode;
+
+       return newm;
 }
 
-void ebt_add_watcher(struct xtables_target *watcher,
-                    struct iptables_command_state *cs)
+struct xtables_target *ebt_add_watcher(struct xtables_target *watcher,
+                                      struct iptables_command_state *cs)
 {
        struct ebt_match *newnode, **matchp;
        struct xtables_target *clone;
@@ -425,6 +427,8 @@ void ebt_add_watcher(struct xtables_target *watcher,
        for (matchp = &cs->match_list; *matchp; matchp = &(*matchp)->next)
                ;
        *matchp = newnode;
+
+       return clone;
 }
 
 int ebt_command_default(struct iptables_command_state *cs,
@@ -476,8 +480,8 @@ int ebt_command_default(struct iptables_command_state *cs,
                if (cs->c < m->option_offset ||
                    cs->c >= m->option_offset + XT_OPTION_OFFSET_SCALE)
                        continue;
+               m = ebt_add_match(m, cs);
                xtables_option_mpcall(cs->c, cs->argv, ebt_invert, m, &cs->eb);
-               ebt_add_match(m, cs);
                return 0;
        }
 
@@ -491,8 +495,8 @@ int ebt_command_default(struct iptables_command_state *cs,
                if (cs->c < t->option_offset ||
                    cs->c >= t->option_offset + XT_OPTION_OFFSET_SCALE)
                        continue;
+               t = ebt_add_watcher(t, cs);
                xtables_option_tpcall(cs->c, cs->argv, ebt_invert, t, &cs->eb);
-               ebt_add_watcher(t, cs);
                return 0;
        }
        if (cs->c == ':')