Hello,
I agree this is a bug.
On Sat, Apr 18, 2026 at 08:33:44PM +0300, Atanas Vladimirov wrote:
</snip>
>
> Index: sys/net/pf_if.c
> ===================================================================
> --- sys/net/pf_if.c
> +++ sys/net/pf_if.c
> @@ -852,8 +852,17 @@ pfi_set_flags(const char *name, int flags)
> } else
> panic("%s pfi_kif_get() returned NULL\n",
> __func__);
> - } else
> + } else {
> p->pfik_flags_new = p->pfik_flags | flags;
> + /*
> + * The kif already existed (created by
> + * pfi_attach_ifnet()/pfi_attach_ifgroup()). Take a
> + * flag ref so it survives a transient disappearance
> + * of the backing ifnet/ifgroup, e.g. vmd(8) destroying
> + * and recreating tap(4) across a VM reboot.
> + */
> + if (ISSET(flags, PFI_IFLAG_SKIP) &&
> + p->pfik_flagrefs == 0)
> + pfi_kif_ref(p, PFI_KIF_REF_FLAG);
> + }
> } else {
> RB_FOREACH(p, pfi_ifhead, &pfi_ifs)
> p->pfik_flags_new = p->pfik_flags | flags;
>
however diff above triggered an assert() on one of my test boxes:
kernel diagnostic assertion "(p->pfik_flagrefs == 0) || (p->pfik_flagref
s == 1)" failed: file "/home/sashan/src.sashan/sys/net/pf_if.c", line 901
Stopped at db_enter+0x14: popq %rbp
TID PID UID PRFLAGS PFLAGS CPU COMMAND
* 30662 18016 0 0x3 0 0K pfctl
db_enter() at db_enter+0x14
panic(ffffffff82672a84) at panic+0xd5
__assert(ffffffff826aeb78,ffffffff8270f923,385,ffffffff82628d6b) at
__assert+0x
29
pfi_clear_flags(ffff80001c8d6200,100) at pfi_clear_flags+0x206
pfioctl(14900,c028445a,ffff80001c8d6200,3,ffff80001c7f14d8) at pfioctl+0xe0f
VOP_IOCTL(fffffd802b6e6cd0,c028445a,ffff80001c8d6200,3,fffffd8004ffda90,ffff800
01c7f14d8) at VOP_IOCTL+0x60
vn_ioctl(fffffd802909ebd0,c028445a,ffff80001c8d6200,ffff80001c7f14d8) at
vn_ioc
tl+0x86
sys_ioctl(ffff80001c7f14d8,ffff80001c8d63b0,ffff80001c8d6330) at
sys_ioctl+0x30
e
syscall(ffff80001c8d63b0) at syscall+0x5f9
Xsyscall() at Xsyscall+0x128
the /etc/pf.conf on that test box contained two 'set skip on' statements
for lo0 interface. Diff below avoids the assert on that box.
Taking a closer look at pfi_set_flags() function I think the branch
with RB_FOREACH() should be removed. The pfctl(8) always fills name
so that code is never reached on behalf of pfctl. Also the RB_FOREACH()
loop body suffers from the same bug we attempt to fix here.
OK ?
thanks and
regards
sashan
--------8<---------------8<---------------8<------------------8<--------
diff --git a/sys/net/pf_if.c b/sys/net/pf_if.c
index 0c6681e757e..bba7237c904 100644
--- a/sys/net/pf_if.c
+++ b/sys/net/pf_if.c
@@ -854,8 +854,22 @@ pfi_set_flags(const char *name, int flags)
} else
panic("%s pfi_kif_get() returned NULL\n",
__func__);
- } else
+ } else {
+ /*
+ * pf.conf may accidentally contain two set skip on ...
+ * statements. For example:
+ * set skip lo
+ * set skip lo
+ * We need to grab reference only when skip flag is
+ * set to avoid tripping assert pfi_clear_flags()
+ */
+ if (ISSET(flags, PFI_IFLAG_SKIP) &&
+ !ISSET(p->pfik_flags_new, PFI_IFLAG_SKIP) &&
+ !ISSET(p->pfik_flags, PFI_IFLAG_SKIP))
+ pfi_kif_ref(p, PFI_KIF_REF_FLAG);
+
p->pfik_flags_new = p->pfik_flags | flags;
+ }
} else {
RB_FOREACH(p, pfi_ifhead, &pfi_ifs)
p->pfik_flags_new = p->pfik_flags | flags;