git: 3524dfd74144 - main - pfctl: Fail to parse rules with invalid ranges

From: Kristof Provost <kp_at_FreeBSD.org>
Date: Tue, 15 Jul 2025 10:07:54 UTC
The branch main has been updated by kp:

URL: https://cgit.FreeBSD.org/src/commit/?id=3524dfd74144b7e9ad43f4317d494bf41645bb45

commit 3524dfd74144b7e9ad43f4317d494bf41645bb45
Author:     Kristof Provost <kp@FreeBSD.org>
AuthorDate: 2025-07-08 09:39:25 +0000
Commit:     Kristof Provost <kp@FreeBSD.org>
CommitDate: 2025-07-15 07:55:29 +0000

    pfctl: Fail to parse rules with invalid ranges
    
    This makes pfctl(8) detect bogus ranges (with and without `-n') before
    loading the ruleset and completes the previous commit.
    
    OK sashan sthen
    
    Obtained from:  OpenBSD, kn <kn@openbsd.org>, 123a1e155c
    Sponsored by:   Rubicon Communications, LLC ("Netgate")
---
 sbin/pfctl/parse.y | 39 ++++++++++++++++++++++++++++++++++++---
 1 file changed, 36 insertions(+), 3 deletions(-)

diff --git a/sbin/pfctl/parse.y b/sbin/pfctl/parse.y
index 4286b6149ac6..a8a165d336fa 100644
--- a/sbin/pfctl/parse.y
+++ b/sbin/pfctl/parse.y
@@ -367,6 +367,7 @@ static struct node_fairq_opts	 fairq_opts;
 static struct node_state_opt	*keep_state_defaults = NULL;
 static struct pfctl_watermarks	 syncookie_opts;
 
+int		 validate_range(uint8_t, uint16_t, uint16_t);
 int		 disallow_table(struct node_host *, const char *);
 int		 disallow_urpf_failed(struct node_host *, const char *);
 int		 disallow_alias(struct node_host *, const char *);
@@ -3825,9 +3826,14 @@ port_item	: portrange			{
 				err(1, "port_item: calloc");
 			$$->port[0] = $1.a;
 			$$->port[1] = $1.b;
-			if ($1.t)
+			if ($1.t) {
 				$$->op = PF_OP_RRG;
-			else
+				if (validate_range($$->op, $$->port[0],
+				    $$->port[1])) {
+					yyerror("invalid port range");
+					YYERROR;
+				}
+			} else
 				$$->op = PF_OP_EQ;
 			$$->next = NULL;
 			$$->tail = $$;
@@ -3844,6 +3850,10 @@ port_item	: portrange			{
 			$$->port[0] = $2.a;
 			$$->port[1] = $2.b;
 			$$->op = $1;
+			if (validate_range($$->op, $$->port[0], $$->port[1])) {
+				yyerror("invalid port range");
+				YYERROR;
+			}
 			$$->next = NULL;
 			$$->tail = $$;
 		}
@@ -3859,6 +3869,10 @@ port_item	: portrange			{
 			$$->port[0] = $1.a;
 			$$->port[1] = $3.a;
 			$$->op = $2;
+			if (validate_range($$->op, $$->port[0], $$->port[1])) {
+				yyerror("invalid port range");
+				YYERROR;
+			}
 			$$->next = NULL;
 			$$->tail = $$;
 		}
@@ -5196,6 +5210,19 @@ yyerror(const char *fmt, ...)
 	return (0);
 }
 
+int
+validate_range(uint8_t op, uint16_t p1, uint16_t p2)
+{
+	uint16_t a = ntohs(p1);
+	uint16_t b = ntohs(p2);
+
+	if ((op == PF_OP_RRG && a > b) ||  /* 34:12,  i.e. none */
+	    (op == PF_OP_IRG && a >= b) || /* 34><12, i.e. none */
+	    (op == PF_OP_XRG && a > b))    /* 34<>22, i.e. all */
+		return 1;
+	return 0;
+}
+
 int
 disallow_table(struct node_host *h, const char *fmt)
 {
@@ -6018,8 +6045,14 @@ apply_rdr_ports(struct pfctl_rule *r, struct pfctl_pool *rpool, struct redirspec
 	if (!rs->rport.b && rs->rport.t) {
 		rpool->proxy_port[1] = ntohs(rs->rport.a) +
 		    (ntohs(r->dst.port[1]) - ntohs(r->dst.port[0]));
-	} else
+	} else {
+		if (validate_range(rs->rport.t, rs->rport.a,
+		    rs->rport.b)) {
+			yyerror("invalid rdr-to port range");
+			return (1);
+		}
 		r->rdr.proxy_port[1] = ntohs(rs->rport.b);
+	}
 
 	if (rs->pool_opts.staticport) {
 		yyerror("the 'static-port' option is only valid with nat rules");