According to the documentation, pfctl is supposed to remove all anchors if there are no rules beneath them. This behavior aligns with the specifications up to Big Sur. However, starting from Ventura and onward, pfctl retains anchors even after flushing out all rules, states, tables, etc. I think this is a huge leaking issue. The only way to remove all the zombie anchors are reboot the system.
Repro steps.
Setup.
/etc/pf.confg scrub-anchor "com.apple/*" nat-anchor "com.apple/*" rdr-anchor "com.apple/*" dummynet-anchor "com.apple/*" anchor "com.apple/*" load anchor "com.apple" from "/etc/pf.anchors/com.apple" anchor "com.foo/*" load anchor "com.foo" from "/etc/pf.anchors/com.foo"
/etc/pf.anchors/com.foo anchor "sub_foo" in inet all { block drop out all pass out log (user) quick inet from any to any flags any keep state pass out log (user) quick inet from any to any no state }
Now here is the repro steps.
1. Load rules pfctl -f /etc/pf.conf 2. Check anchors pfctl -sA com.apple com.foo 3. Check subanchors of com.foo pfctl -a "com.foo" -sA com.foo/sub_foo 4. Check rules of subanchors sub_foo pfctl -a "com.foo" -sr anchor "com.foo/sub_foo" in inet all 5. Remove all rules, and states whatever pfctl -a "com.foo" -Fa rules cleared nat cleared dummynet cleared 0 tables deleted. 6. Check if the anchors is gone pfctl -a "com.foo" -sA com.foo/sub_foo 7. Since #6 anchor didn’t go check and see if the rules underneath it. pfctl -a "com.foo" -sr No rules.