diff --git a/sys/netpfil/pf/pf_table.c b/sys/netpfil/pf/pf_table.c index d11401b6f5a7..7fc3b0a6380b 100644 --- a/sys/netpfil/pf/pf_table.c +++ b/sys/netpfil/pf/pf_table.c @@ -651,16 +651,23 @@ pfr_clr_astats(struct pfr_table *tbl, struct pfr_addr *addr, int size, kt = pfr_lookup_table(tbl); if (kt == NULL || !(kt->pfrkt_flags & PFR_TFLAG_ACTIVE)) return (ESRCH); - SLIST_INIT(&workq); for (i = 0, ad = addr; i < size; i++, ad++) { if (pfr_validate_addr(ad)) senderr(EINVAL); p = pfr_lookup_addr(kt, ad, 1); + if (p != NULL) + p->pfrke_mark = 0; + } + SLIST_INIT(&workq); + for (i = 0, ad = addr; i < size; i++, ad++) { + p = pfr_lookup_addr(kt, ad, 1); if (flags & PFR_FLAG_FEEDBACK) { ad->pfra_fback = (p != NULL) ? PFR_FB_CLEARED : PFR_FB_NONE; } - if (p != NULL) { + /* An address given more than once is cleared once. */ + if (p != NULL && !p->pfrke_mark) { + p->pfrke_mark = 1; SLIST_INSERT_HEAD(&workq, p, pfrke_workq); xzero++; } diff --git a/tests/sys/netpfil/pf/table.sh b/tests/sys/netpfil/pf/table.sh index c5c9c45c9d3d..7c8cb084b48a 100644 --- a/tests/sys/netpfil/pf/table.sh +++ b/tests/sys/netpfil/pf/table.sh @@ -294,6 +294,39 @@ zero_all_cleanup() pft_cleanup } +atf_test_case "zero_twice" "cleanup" +zero_twice_head() +{ + atf_set descr 'Test zeroing an address that is given twice' + atf_set require.user root + atf_set timeout 30 +} + +zero_twice_body() +{ + pft_init + + vnet_mkjail alcatraz + jexec alcatraz pfctl -e + + pft_set_rules alcatraz \ + "table counters { 192.0.2.1, 192.0.2.3 }" \ + "pass in from to any" + + # This used to hang the kernel with the rules lock held: + # pfr_clr_astats() put the entry on its work queue twice. + atf_check -s exit:0 -e "match:1/2 addresses cleared." \ + jexec alcatraz pfctl -t foo -T zero 192.0.2.1 192.0.2.1 + atf_check -s exit:0 -e "match:2/4 addresses cleared." \ + jexec alcatraz pfctl -t foo -T zero 192.0.2.3 192.0.2.1 \ + 192.0.2.3 192.0.2.5 +} + +zero_twice_cleanup() +{ + pft_cleanup +} + atf_test_case "reset_nonzero" "cleanup" reset_nonzero_head() { @@ -923,6 +956,7 @@ atf_init_test_cases() atf_add_test_case "match_counters" atf_add_test_case "zero_one" atf_add_test_case "zero_all" + atf_add_test_case "zero_twice" atf_add_test_case "reset_nonzero" atf_add_test_case "pr251414" atf_add_test_case "automatic"