The branch main has been updated by ae:

URL: 
https://cgit.FreeBSD.org/src/commit/?id=91ed876385d4531b6ab2f9176be969318e1aefc1

commit 91ed876385d4531b6ab2f9176be969318e1aefc1
Author:     Andrey V. Elsukov <a...@freebsd.org>
AuthorDate: 2025-07-22 08:02:17 +0000
Commit:     Andrey V. Elsukov <a...@freebsd.org>
CommitDate: 2025-08-03 09:46:51 +0000

    ipfw: forbid adding keep-state rules that depend on tablearg
    
    tablearg value is determined after making table lookup. When we
    applying rule action that uses dynamic state, such lookup was
    not done and thus rule action can not determine what table and
    what value should be used as tablearg.
    To prevent this add check for such rules and return error when
    they are added.
    
    Obtained from:  Yandex LLC
    Sponsored by:   Yandex LLC
    Differential Revision: https://reviews.freebsd.org/D51458
---
 sys/netpfil/ipfw/ip_fw_private.h |  1 +
 sys/netpfil/ipfw/ip_fw_sockopt.c | 31 +++++++++++++++++++++++++++++++
 2 files changed, 32 insertions(+)

diff --git a/sys/netpfil/ipfw/ip_fw_private.h b/sys/netpfil/ipfw/ip_fw_private.h
index 79b3ed43f63b..c490d2849a7d 100644
--- a/sys/netpfil/ipfw/ip_fw_private.h
+++ b/sys/netpfil/ipfw/ip_fw_private.h
@@ -489,6 +489,7 @@ struct obj_idx {
 
 struct rule_check_info {
        uint16_t        flags;          /* rule-specific check flags */
+#define        IPFW_RCIFLAG_HAS_STATE          0x0001
        uint16_t        object_opcodes; /* num of opcodes referencing objects */
        uint16_t        urule_numoff;   /* offset of rulenum in bytes */
        uint8_t         version;        /* rule version */
diff --git a/sys/netpfil/ipfw/ip_fw_sockopt.c b/sys/netpfil/ipfw/ip_fw_sockopt.c
index 19f5fff2749a..5d57759ffb00 100644
--- a/sys/netpfil/ipfw/ip_fw_sockopt.c
+++ b/sys/netpfil/ipfw/ip_fw_sockopt.c
@@ -1311,6 +1311,9 @@ ipfw_check_rule(struct ip_fw_rule *rule, size_t size,
        return (check_ipfw_rule_body(rule->cmd, rule->cmd_len, ci));
 }
 
+#define        CHECK_TARG(a, c)        \
+    ((a) == IP_FW_TARG && ((c)->flags & IPFW_RCIFLAG_HAS_STATE))
+
 enum ipfw_opcheck_result
 ipfw_check_opcode(ipfw_insn **pcmd, int *plen, struct rule_check_info *ci)
 {
@@ -1326,6 +1329,7 @@ ipfw_check_opcode(ipfw_insn **pcmd, int *plen, struct 
rule_check_info *ci)
                if (cmdlen != F_INSN_SIZE(ipfw_insn_kidx))
                        return (BAD_SIZE);
                ci->object_opcodes++;
+               ci->flags |= IPFW_RCIFLAG_HAS_STATE;
                break;
        case O_PROTO:
        case O_IP_SRC_ME:
@@ -1410,6 +1414,8 @@ ipfw_check_opcode(ipfw_insn **pcmd, int *plen, struct 
rule_check_info *ci)
                                cmd->arg1 & 0x7FFF);
                        return (FAILED);
                }
+               if (CHECK_TARG(cmd->arg1, ci))
+                       goto bad_targ;
                return (CHECK_ACTION);
 
        case O_UID:
@@ -1518,11 +1524,16 @@ ipfw_check_opcode(ipfw_insn **pcmd, int *plen, struct 
rule_check_info *ci)
        case O_QUEUE:
                if (cmdlen != F_INSN_SIZE(ipfw_insn))
                        return (BAD_SIZE);
+               if (CHECK_TARG(cmd->arg1, ci))
+                       goto bad_targ;
                return (CHECK_ACTION);
 
        case O_FORWARD_IP:
                if (cmdlen != F_INSN_SIZE(ipfw_insn_sa))
                        return (BAD_SIZE);
+               if (insntoc(cmd, sa)->sa.sin_addr.s_addr == INADDR_ANY &&
+                   (ci->flags & IPFW_RCIFLAG_HAS_STATE))
+                       goto bad_targ;
                return (CHECK_ACTION);
 #ifdef INET6
        case O_FORWARD_IP6:
@@ -1537,6 +1548,8 @@ ipfw_check_opcode(ipfw_insn **pcmd, int *plen, struct 
rule_check_info *ci)
                        return (FAILED);
                if (cmdlen != F_INSN_SIZE(ipfw_insn))
                        return (BAD_SIZE);
+               if (CHECK_TARG(cmd->arg1, ci))
+                       goto bad_targ;
                return (CHECK_ACTION);
        case O_NETGRAPH:
        case O_NGTEE:
@@ -1544,12 +1557,16 @@ ipfw_check_opcode(ipfw_insn **pcmd, int *plen, struct 
rule_check_info *ci)
                        return (FAILED);
                if (cmdlen != F_INSN_SIZE(ipfw_insn))
                        return (BAD_SIZE);
+               if (CHECK_TARG(cmd->arg1, ci))
+                       goto bad_targ;
                return (CHECK_ACTION);
        case O_NAT:
                if (!IPFW_NAT_LOADED)
                        return (FAILED);
                if (cmdlen != F_INSN_SIZE(ipfw_insn_nat))
                        return (BAD_SIZE);
+               if (CHECK_TARG(cmd->arg1, ci))
+                       goto bad_targ;
                return (CHECK_ACTION);
 
        case O_SKIPTO:
@@ -1557,6 +1574,11 @@ ipfw_check_opcode(ipfw_insn **pcmd, int *plen, struct 
rule_check_info *ci)
        case O_SETMARK:
                if (cmdlen != F_INSN_SIZE(ipfw_insn_u32))
                        return (BAD_SIZE);
+               /* O_CALLRETURN + F_NOT means 'return' opcode. */
+               if (cmd->opcode != O_CALLRETURN || (cmd->len & F_NOT) == 0) {
+                       if (CHECK_TARG(insntoc(cmd, u32)->d[0], ci))
+                               goto bad_targ;
+               }
                return (CHECK_ACTION);
 
        case O_CHECK_STATE:
@@ -1577,6 +1599,8 @@ ipfw_check_opcode(ipfw_insn **pcmd, int *plen, struct 
rule_check_info *ci)
        case O_REASS:
                if (cmdlen != F_INSN_SIZE(ipfw_insn))
                        return (BAD_SIZE);
+               if (cmd->opcode == O_SETDSCP && CHECK_TARG(cmd->arg1, ci))
+                       goto bad_targ;
                return (CHECK_ACTION);
 #ifdef INET6
        case O_IP6_SRC:
@@ -1627,6 +1651,13 @@ ipfw_check_opcode(ipfw_insn **pcmd, int *plen, struct 
rule_check_info *ci)
                }
        }
        return (SUCCESS);
+bad_targ:
+       /*
+        * For dynamic states we can not correctly initialize tablearg value,
+        * because we don't go through rule's opcodes except rule action.
+        */
+       printf("ipfw: tablearg is not allowed with dynamic states\n");
+       return (FAILED);
 }
 
 static __noinline int

Reply via email to