git: b70fadca623f - main - pf: fix dealing with 0 limits

From: Kristof Provost <kp_at_FreeBSD.org>
Date: Thu, 08 May 2025 13:10:44 UTC
The branch main has been updated by kp:

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

commit b70fadca623f1ce62c34c67270feae6cf48ca4ba
Author:     Kristof Provost <kp@FreeBSD.org>
AuthorDate: 2025-05-07 08:43:14 +0000
Commit:     Kristof Provost <kp@FreeBSD.org>
CommitDate: 2025-05-08 13:10:25 +0000

    pf: fix dealing with 0 limits
    
    uma doesn't like setting a limit on a zone which previously had none.
    At startup pf applies the default limit to all zones, so we can assume a limit
    was always set. We cope with the uma limitation by translating a limit of '0'
    (i.e. unlimited) to INT_MAX. This is high enough that we'll never realistically
    hit this limit, but it keeps uma happy.
    
    Add a test case to provoke this, and while we're here also fix syncookie
    handling of a 0 state limit.
    
    See also:       d53927b0bae45
    Reported-by:    syzbot+02b784f183f79d4c07e4@syzkaller.appspotmail.com
    Sponsored by:   Rubicon Communications, LLC ("Netgate")
---
 lib/libpfctl/libpfctl.c        |  6 +++++
 sys/netpfil/pf/pf_ioctl.c      |  3 ++-
 tests/sys/netpfil/pf/limits.sh | 53 ++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 61 insertions(+), 1 deletion(-)

diff --git a/lib/libpfctl/libpfctl.c b/lib/libpfctl/libpfctl.c
index 271fb33babfe..a4afa26f0afe 100644
--- a/lib/libpfctl/libpfctl.c
+++ b/lib/libpfctl/libpfctl.c
@@ -2271,6 +2271,9 @@ pfctl_set_syncookies(int dev, const struct pfctl_syncookies *s)
 	if (ret != 0)
 		return (ret);
 
+	if (state_limit == 0)
+		state_limit = INT_MAX;
+
 	lim = state_limit;
 	hi = lim * s->highwater / 100;
 	lo = lim * s->lowwater / 100;
@@ -2311,6 +2314,9 @@ pfctl_get_syncookies(int dev, struct pfctl_syncookies *s)
 	if (ret != 0)
 		return (ret);
 
+	if (state_limit == 0)
+		state_limit = INT_MAX;
+
 	bzero(s, sizeof(*s));
 
 	nvl = nvlist_create(0);
diff --git a/sys/netpfil/pf/pf_ioctl.c b/sys/netpfil/pf/pf_ioctl.c
index 5c3aec906b79..bd3c2b93c954 100644
--- a/sys/netpfil/pf/pf_ioctl.c
+++ b/sys/netpfil/pf/pf_ioctl.c
@@ -2537,7 +2537,8 @@ pf_ioctl_set_limit(int index, unsigned int limit, unsigned int *old_limit)
 		PF_RULES_WUNLOCK();
 		return (EINVAL);
 	}
-	uma_zone_set_max(V_pf_limits[index].zone, limit);
+	uma_zone_set_max(V_pf_limits[index].zone,
+	    limit == 0 ? INT_MAX : limit);
 	if (old_limit != NULL)
 		*old_limit = V_pf_limits[index].limit;
 	V_pf_limits[index].limit = limit;
diff --git a/tests/sys/netpfil/pf/limits.sh b/tests/sys/netpfil/pf/limits.sh
index 474684bef660..69f0b6af2ccf 100644
--- a/tests/sys/netpfil/pf/limits.sh
+++ b/tests/sys/netpfil/pf/limits.sh
@@ -60,7 +60,60 @@ basic_cleanup()
 	pft_cleanup
 }
 
+atf_test_case "zero" "cleanup"
+zero_head()
+{
+	atf_set descr 'Test changing a limit from zero on an in-use zone'
+	atf_set require.user root
+}
+
+zero_body()
+{
+	pft_init
+
+	epair=$(vnet_mkepair)
+	ifconfig ${epair}b 192.0.2.2/24 up
+
+	vnet_mkjail alcatraz ${epair}a
+	jexec alcatraz ifconfig ${epair}a 192.0.2.1/24 up
+
+	atf_check -s exit:0 -o ignore \
+	    ping -c 3 192.0.2.1
+
+	jexec alcatraz pfctl -e
+	# Set no limit
+	pft_set_rules noflush alcatraz \
+		"set limit states 0" \
+		"pass"
+
+	# Check that we really report no limit
+	atf_check -s exit:0 -o 'match:states        hard limit        0' \
+	    jexec alcatraz pfctl -sa
+
+	# Create a state
+	atf_check -s exit:0 -o ignore \
+	    ping -c 3 192.0.2.1
+
+	# Limit states
+	pft_set_rules noflush alcatraz \
+		"set limit states 1000" \
+		"pass"
+
+	# And create a new state
+	atf_check -s exit:0 -o ignore \
+	    ping -c 3 192.0.2.1
+
+	atf_check -s exit:0 -o 'match:states        hard limit     1000' \
+	    jexec alcatraz pfctl -sa
+}
+
+zero_cleanup()
+{
+	pft_cleanup
+}
+
 atf_init_test_cases()
 {
 	atf_add_test_case "basic"
+	atf_add_test_case "zero"
 }