Re: another syzkaller problem in pf

2022-06-26 Thread Alexander Bluhm
On Sun, Jun 26, 2022 at 12:02:41PM +0200, Moritz Buhl wrote:
> On Tue, May 03, 2022 at 09:31:51PM +0200, Alexander Bluhm wrote:
> ...
> > The code is too complex to be sure what the reason of the syzkaller
> > panic is.  Sleep in malloc is correct anyway and may improve the
> > situation.
> > 
> > Functions with argument values 0 or 1 are hard to read.  It would
> > be much nicer to pass M_WAITOK or M_NOWAIT.  And the variable name
> > "intr" does not make sense anymore.  pf does not run in interrupt
> > context.  Call it "mflags" like in pfi_kif_alloc().  Or "wait" like
> > in other functions.
> > 
> > Could you cleanup that also?
> 
> I renamed the intr parameter for pfr_create_ktable and pfr_attach_table
> and passed it further up the call stack.
> In pf_addr_setup I noticed that pf_tbladdr_setup also used to set intr.
> I changed it to wait for both, pfi_dynaddr_setup and pf_tbladdr_setup.
> 
> Now it should be a little easier to notice sleeping points in pf_ioctl.c.
> 
> OK?

OK bluhm@

> Index: sys/net/pf.c
> ===
> RCS file: /cvs/src/sys/net/pf.c,v
> retrieving revision 1.1133
> diff -u -p -r1.1133 pf.c
> --- sys/net/pf.c  13 Jun 2022 12:48:00 -  1.1133
> +++ sys/net/pf.c  26 Jun 2022 09:21:21 -
> @@ -1585,11 +1585,11 @@ pf_purge_expired_states(u_int32_t maxche
>  }
>  
>  int
> -pf_tbladdr_setup(struct pf_ruleset *rs, struct pf_addr_wrap *aw)
> +pf_tbladdr_setup(struct pf_ruleset *rs, struct pf_addr_wrap *aw, int wait)
>  {
>   if (aw->type != PF_ADDR_TABLE)
>   return (0);
> - if ((aw->p.tbl = pfr_attach_table(rs, aw->v.tblname, 1)) == NULL)
> + if ((aw->p.tbl = pfr_attach_table(rs, aw->v.tblname, wait)) == NULL)
>   return (1);
>   return (0);
>  }
> Index: sys/net/pf_if.c
> ===
> RCS file: /cvs/src/sys/net/pf_if.c,v
> retrieving revision 1.105
> diff -u -p -r1.105 pf_if.c
> --- sys/net/pf_if.c   16 May 2022 13:31:19 -  1.105
> +++ sys/net/pf_if.c   26 Jun 2022 09:38:43 -
> @@ -423,7 +423,7 @@ pfi_match_addr(struct pfi_dynaddr *dyn, 
>  }
>  
>  int
> -pfi_dynaddr_setup(struct pf_addr_wrap *aw, sa_family_t af)
> +pfi_dynaddr_setup(struct pf_addr_wrap *aw, sa_family_t af, int wait)
>  {
>   struct pfi_dynaddr  *dyn;
>   char tblname[PF_TABLE_NAME_SIZE];
> @@ -432,8 +432,7 @@ pfi_dynaddr_setup(struct pf_addr_wrap *a
>  
>   if (aw->type != PF_ADDR_DYNIFTL)
>   return (0);
> - if ((dyn = pool_get(_addr_pl, PR_WAITOK | PR_LIMITFAIL | PR_ZERO))
> - == NULL)
> + if ((dyn = pool_get(_addr_pl, wait|PR_LIMITFAIL|PR_ZERO)) == NULL)
>   return (1);
>  
>   if (!strcmp(aw->v.ifname, "self"))
> @@ -466,7 +465,7 @@ pfi_dynaddr_setup(struct pf_addr_wrap *a
>   goto _bad;
>   }
>  
> - if ((dyn->pfid_kt = pfr_attach_table(ruleset, tblname, 1)) == NULL) {
> + if ((dyn->pfid_kt = pfr_attach_table(ruleset, tblname, wait)) == NULL) {
>   rv = 1;
>   goto _bad;
>   }
> Index: sys/net/pf_ioctl.c
> ===
> RCS file: /cvs/src/sys/net/pf_ioctl.c,v
> retrieving revision 1.381
> diff -u -p -r1.381 pf_ioctl.c
> --- sys/net/pf_ioctl.c10 May 2022 23:12:25 -  1.381
> +++ sys/net/pf_ioctl.c26 Jun 2022 09:35:17 -
> @@ -891,8 +891,8 @@ int
>  pf_addr_setup(struct pf_ruleset *ruleset, struct pf_addr_wrap *addr,
>  sa_family_t af)
>  {
> - if (pfi_dynaddr_setup(addr, af) ||
> - pf_tbladdr_setup(ruleset, addr) ||
> + if (pfi_dynaddr_setup(addr, af, PR_WAITOK) ||
> + pf_tbladdr_setup(ruleset, addr, PR_WAITOK) ||
>   pf_rtlabel_add(addr))
>   return (EINVAL);
>  
> @@ -1413,7 +1413,7 @@ pfioctl(dev_t dev, u_long cmd, caddr_t a
>  
>   if (rule->overload_tblname[0]) {
>   if ((rule->overload_tbl = pfr_attach_table(ruleset,
> - rule->overload_tblname, 0)) == NULL)
> + rule->overload_tblname, PR_WAITOK)) == NULL)
>   error = EINVAL;
>   else
>   rule->overload_tbl->pfrkt_flags |= 
> PFR_TFLAG_ACTIVE;
> @@ -1636,7 +1636,8 @@ pfioctl(dev_t dev, u_long cmd, caddr_t a
>  
>   if (newrule->overload_tblname[0]) {
>   newrule->overload_tbl = pfr_attach_table(
> - ruleset, newrule->overload_tblname, 0);
> + ruleset, newrule->overload_tblname,
> + PR_WAITOK);
>   if (newrule->overload_tbl == NULL)
>   error = EINVAL;
>   else
> Index: sys/net/pf_table.c
> 

Re: another syzkaller problem in pf

2022-06-26 Thread Moritz Buhl
On Tue, May 03, 2022 at 09:31:51PM +0200, Alexander Bluhm wrote:
...
> The code is too complex to be sure what the reason of the syzkaller
> panic is.  Sleep in malloc is correct anyway and may improve the
> situation.
> 
> Functions with argument values 0 or 1 are hard to read.  It would
> be much nicer to pass M_WAITOK or M_NOWAIT.  And the variable name
> "intr" does not make sense anymore.  pf does not run in interrupt
> context.  Call it "mflags" like in pfi_kif_alloc().  Or "wait" like
> in other functions.
> 
> Could you cleanup that also?

I renamed the intr parameter for pfr_create_ktable and pfr_attach_table
and passed it further up the call stack.
In pf_addr_setup I noticed that pf_tbladdr_setup also used to set intr.
I changed it to wait for both, pfi_dynaddr_setup and pf_tbladdr_setup.

Now it should be a little easier to notice sleeping points in pf_ioctl.c.

OK?

mbuhl

Index: sys/net/pf.c
===
RCS file: /cvs/src/sys/net/pf.c,v
retrieving revision 1.1133
diff -u -p -r1.1133 pf.c
--- sys/net/pf.c13 Jun 2022 12:48:00 -  1.1133
+++ sys/net/pf.c26 Jun 2022 09:21:21 -
@@ -1585,11 +1585,11 @@ pf_purge_expired_states(u_int32_t maxche
 }
 
 int
-pf_tbladdr_setup(struct pf_ruleset *rs, struct pf_addr_wrap *aw)
+pf_tbladdr_setup(struct pf_ruleset *rs, struct pf_addr_wrap *aw, int wait)
 {
if (aw->type != PF_ADDR_TABLE)
return (0);
-   if ((aw->p.tbl = pfr_attach_table(rs, aw->v.tblname, 1)) == NULL)
+   if ((aw->p.tbl = pfr_attach_table(rs, aw->v.tblname, wait)) == NULL)
return (1);
return (0);
 }
Index: sys/net/pf_if.c
===
RCS file: /cvs/src/sys/net/pf_if.c,v
retrieving revision 1.105
diff -u -p -r1.105 pf_if.c
--- sys/net/pf_if.c 16 May 2022 13:31:19 -  1.105
+++ sys/net/pf_if.c 26 Jun 2022 09:38:43 -
@@ -423,7 +423,7 @@ pfi_match_addr(struct pfi_dynaddr *dyn, 
 }
 
 int
-pfi_dynaddr_setup(struct pf_addr_wrap *aw, sa_family_t af)
+pfi_dynaddr_setup(struct pf_addr_wrap *aw, sa_family_t af, int wait)
 {
struct pfi_dynaddr  *dyn;
char tblname[PF_TABLE_NAME_SIZE];
@@ -432,8 +432,7 @@ pfi_dynaddr_setup(struct pf_addr_wrap *a
 
if (aw->type != PF_ADDR_DYNIFTL)
return (0);
-   if ((dyn = pool_get(_addr_pl, PR_WAITOK | PR_LIMITFAIL | PR_ZERO))
-   == NULL)
+   if ((dyn = pool_get(_addr_pl, wait|PR_LIMITFAIL|PR_ZERO)) == NULL)
return (1);
 
if (!strcmp(aw->v.ifname, "self"))
@@ -466,7 +465,7 @@ pfi_dynaddr_setup(struct pf_addr_wrap *a
goto _bad;
}
 
-   if ((dyn->pfid_kt = pfr_attach_table(ruleset, tblname, 1)) == NULL) {
+   if ((dyn->pfid_kt = pfr_attach_table(ruleset, tblname, wait)) == NULL) {
rv = 1;
goto _bad;
}
Index: sys/net/pf_ioctl.c
===
RCS file: /cvs/src/sys/net/pf_ioctl.c,v
retrieving revision 1.381
diff -u -p -r1.381 pf_ioctl.c
--- sys/net/pf_ioctl.c  10 May 2022 23:12:25 -  1.381
+++ sys/net/pf_ioctl.c  26 Jun 2022 09:35:17 -
@@ -891,8 +891,8 @@ int
 pf_addr_setup(struct pf_ruleset *ruleset, struct pf_addr_wrap *addr,
 sa_family_t af)
 {
-   if (pfi_dynaddr_setup(addr, af) ||
-   pf_tbladdr_setup(ruleset, addr) ||
+   if (pfi_dynaddr_setup(addr, af, PR_WAITOK) ||
+   pf_tbladdr_setup(ruleset, addr, PR_WAITOK) ||
pf_rtlabel_add(addr))
return (EINVAL);
 
@@ -1413,7 +1413,7 @@ pfioctl(dev_t dev, u_long cmd, caddr_t a
 
if (rule->overload_tblname[0]) {
if ((rule->overload_tbl = pfr_attach_table(ruleset,
-   rule->overload_tblname, 0)) == NULL)
+   rule->overload_tblname, PR_WAITOK)) == NULL)
error = EINVAL;
else
rule->overload_tbl->pfrkt_flags |= 
PFR_TFLAG_ACTIVE;
@@ -1636,7 +1636,8 @@ pfioctl(dev_t dev, u_long cmd, caddr_t a
 
if (newrule->overload_tblname[0]) {
newrule->overload_tbl = pfr_attach_table(
-   ruleset, newrule->overload_tblname, 0);
+   ruleset, newrule->overload_tblname,
+   PR_WAITOK);
if (newrule->overload_tbl == NULL)
error = EINVAL;
else
Index: sys/net/pf_table.c
===
RCS file: /cvs/src/sys/net/pf_table.c,v
retrieving revision 1.142
diff -u -p -r1.142 pf_table.c
--- sys/net/pf_table.c  16 Jun 2022 20:47:26 -  1.142
+++ sys/net/pf_table.c  26 Jun 2022 

Re: [External] : Re: another syzkaller problem in pf

2022-05-04 Thread Alexandr Nedvedicky
On Wed, May 04, 2022 at 04:26:18PM +0200, Alexander Bluhm wrote:
> On Wed, May 04, 2022 at 02:21:11PM +0200, Alexandr Nedvedicky wrote:
> > I'm not sure flipping a flag is a right change. In general we don't want
> > to hold NET_LOCK()/PF_LOCK() while waiting for memory.
> 
> - We must not wait for memory when in the packet processing hot path.
>   Drop the packet instead.
> 
> - We should not wait for memory when holding pf lock.  Then we can
>   replace rw lock with a mutex or something else later.
> 
> - Waiting for memory with net lock is unavoidable.  We have to
>   wait when coming from system call.  Doing preallocation may be
>   possible in some cases but code may get too complicated elsewhere.
> 
> If getting the allocation out of the locks is doable here, it could
> be the best solution.

I think it's doable. I'll try to finish the for tables and start sending
diffs hopefully next week.

regards
sashan



Re: [External] : Re: another syzkaller problem in pf

2022-05-04 Thread Alexander Bluhm
On Wed, May 04, 2022 at 02:21:11PM +0200, Alexandr Nedvedicky wrote:
> I'm not sure flipping a flag is a right change. In general we don't want
> to hold NET_LOCK()/PF_LOCK() while waiting for memory.

- We must not wait for memory when in the packet processing hot path.
  Drop the packet instead.

- We should not wait for memory when holding pf lock.  Then we can
  replace rw lock with a mutex or something else later.

- Waiting for memory with net lock is unavoidable.  We have to
  wait when coming from system call.  Doing preallocation may be
  possible in some cases but code may get too complicated elsewhere.

If getting the allocation out of the locks is doable here, it could
be the best solution.

bluhm



Re: [External] : Re: another syzkaller problem in pf

2022-05-04 Thread Alexandr Nedvedicky
On Tue, May 03, 2022 at 09:31:51PM +0200, Alexander Bluhm wrote:
> On Tue, May 03, 2022 at 07:42:34PM +0200, Moritz Buhl wrote:
> > commit 4b3977248902c22d96aaebdb5784840debc2631c
> > Author: mikeb 
> > Date:   Mon Nov 24 13:22:09 2008 +
> > 
> > Fix splasserts seen in pr 5987 by propagating a flag that discribes
> > whether we're called from the interrupt context to the functions
> > performing allocations.
> 
> These days pf was protected with kernel lock and spl.  Both are
> released when sleeping.  Now we have netlock and pflock.  These are
> rwlocks and not released during sleep.  So this old race should not
> exist anymore.
> 
> > And we are not in interrupt context.
> 
> Yes, it is ioctl(2).  I think we should always malloc with M_WAITOK
> when in syscall.  Otherwise userland would have to cope with randomly
> failing syscalls.
> 
> > If this is sound, then the only reason why pfr_destroy_ktable was called
> > is that pool_get is called with PR_NOWAIT.  And then the following diff
> > would help.
> 
> The code is too complex to be sure what the reason of the syzkaller
> panic is.  Sleep in malloc is correct anyway and may improve the
> situation.
> 
> Functions with argument values 0 or 1 are hard to read.  It would
> be much nicer to pass M_WAITOK or M_NOWAIT.  And the variable name
> "intr" does not make sense anymore.  pf does not run in interrupt
> context.  Call it "mflags" like in pfi_kif_alloc().  Or "wait" like
> in other functions.
> 
> Could you cleanup that also?

I have a diff, which touches the same area. It's a work in progress change.
It moves all memory allocations outside of NET_LOCK/PF_LOCK. I'm just
sending it for your reference now.

I'm not sure flipping a flag is a right change. In general we don't want
to hold NET_LOCK()/PF_LOCK() while waiting for memory.

regards
sashan

8<---8<---8<--8<
diff --git a/sys/net/pf_ioctl.c b/sys/net/pf_ioctl.c
index 8315b115474..1f036e1368f 100644
--- a/sys/net/pf_ioctl.c
+++ b/sys/net/pf_ioctl.c
@@ -2217,12 +2217,8 @@ pfioctl(dev_t dev, u_long cmd, caddr_t addr, int flags, 
struct proc *p)
error = ENODEV;
goto fail;
}
-   NET_LOCK();
-   PF_LOCK();
error = pfr_add_tables(io->pfrio_buffer, io->pfrio_size,
>pfrio_nadd, io->pfrio_flags | PFR_FLAG_USERIOCTL);
-   PF_UNLOCK();
-   NET_UNLOCK();
break;
}
 
diff --git a/sys/net/pf_table.c b/sys/net/pf_table.c
index fb23bcabe04..cbaa728b105 100644
--- a/sys/net/pf_table.c
+++ b/sys/net/pf_table.c
@@ -189,6 +189,7 @@ void pfr_clstats_ktable(struct 
pfr_ktable *, time_t, int);
 struct pfr_ktable  *pfr_create_ktable(struct pfr_table *, time_t, int,
int);
 voidpfr_destroy_ktables(struct pfr_ktableworkq *, int);
+voidpfr_destroy_ktables_aux(struct pfr_ktableworkq *);
 voidpfr_destroy_ktable(struct pfr_ktable *, int);
 int pfr_ktable_compare(struct pfr_ktable *,
struct pfr_ktable *);
@@ -1493,14 +1494,16 @@ pfr_clr_tables(struct pfr_table *filter, int *ndel, int 
flags)
 int
 pfr_add_tables(struct pfr_table *tbl, int size, int *nadd, int flags)
 {
-   struct pfr_ktableworkq   addq, changeq;
-   struct pfr_ktable   *p, *q, *r, key;
+   struct pfr_ktableworkq   addq, changeq, auxq;
+   struct pfr_ktable   *p, *q, *r, *n, *w, key;
int  i, rv, xadd = 0;
time_t   tzero = gettime();
 
ACCEPT_FLAGS(flags, PFR_FLAG_DUMMY);
SLIST_INIT();
SLIST_INIT();
+   SLIST_INIT();
+   /* pre-allocate all memory outside of locks */
for (i = 0; i < size; i++) {
YIELD(flags & PFR_FLAG_USERIOCTL);
if (COPYIN(tbl+i, _t, sizeof(key.pfrkt_t), flags))
@@ -1509,65 +1512,140 @@ pfr_add_tables(struct pfr_table *tbl, int size, int 
*nadd, int flags)
flags & PFR_FLAG_USERIOCTL))
senderr(EINVAL);
key.pfrkt_flags |= PFR_TFLAG_ACTIVE;
-   p = RB_FIND(pfr_ktablehead, _ktables, );
-   if (p == NULL) {
-   p = pfr_create_ktable(_t, tzero, 1,
-   !(flags & PFR_FLAG_USERIOCTL));
-   if (p == NULL)
-   senderr(ENOMEM);
-   SLIST_FOREACH(q, , pfrkt_workq) {
-   if (!pfr_ktable_compare(p, q)) {
-   pfr_destroy_ktable(p, 0);
-   goto _skip;
-   }
-   }
-   SLIST_INSERT_HEAD(, p, pfrkt_workq);
-   xadd++;
-  

Re: another syzkaller problem in pf

2022-05-03 Thread Alexander Bluhm
On Tue, May 03, 2022 at 07:42:34PM +0200, Moritz Buhl wrote:
> commit 4b3977248902c22d96aaebdb5784840debc2631c
> Author: mikeb 
> Date:   Mon Nov 24 13:22:09 2008 +
> 
> Fix splasserts seen in pr 5987 by propagating a flag that discribes
> whether we're called from the interrupt context to the functions
> performing allocations.

These days pf was protected with kernel lock and spl.  Both are
released when sleeping.  Now we have netlock and pflock.  These are
rwlocks and not released during sleep.  So this old race should not
exist anymore.

> And we are not in interrupt context.

Yes, it is ioctl(2).  I think we should always malloc with M_WAITOK
when in syscall.  Otherwise userland would have to cope with randomly
failing syscalls.

> If this is sound, then the only reason why pfr_destroy_ktable was called
> is that pool_get is called with PR_NOWAIT.  And then the following diff
> would help.

The code is too complex to be sure what the reason of the syzkaller
panic is.  Sleep in malloc is correct anyway and may improve the
situation.

Functions with argument values 0 or 1 are hard to read.  It would
be much nicer to pass M_WAITOK or M_NOWAIT.  And the variable name
"intr" does not make sense anymore.  pf does not run in interrupt
context.  Call it "mflags" like in pfi_kif_alloc().  Or "wait" like
in other functions.

Could you cleanup that also?

bluhm

> Index: pf_if.c
> ===
> RCS file: /cvs/src/sys/net/pf_if.c,v
> retrieving revision 1.104
> diff -u -p -r1.104 pf_if.c
> --- pf_if.c   29 Apr 2022 09:55:43 -  1.104
> +++ pf_if.c   3 May 2022 16:01:23 -
> @@ -464,7 +464,7 @@ pfi_dynaddr_setup(struct pf_addr_wrap *a
>   goto _bad;
>   }
>  
> - if ((dyn->pfid_kt = pfr_attach_table(ruleset, tblname, 1)) == NULL) {
> + if ((dyn->pfid_kt = pfr_attach_table(ruleset, tblname, 0)) == NULL) {
>   rv = 1;
>   goto _bad;
>   }



Re: another syzkaller problem in pf

2022-05-03 Thread Ted Unangst
On 2022-05-03, Moritz Buhl wrote:
> Hi tech@,
> 
> Syzkaller found a few crashes in pf_anchor_global_RB_REMOVE.
> https://syzkaller.appspot.com/bug?id=a97f712331903ce38b8c084a489818b9bb5c6fcb
> and also
> https://syzkaller.appspot.com/text?tag=CrashLog=15ace9aaf0
> 
> The call stack is something like this:
> pf_anchor_global_RB_REMOVE
> pf_remove_if_empty_ruleset
> pfi_dynaddr_setup
> pfioctl
> 
> I believe this is caused by two calls to pf_remove_if_empty_ruleset
> in pfi_dynaddr_setup and a possibly pfr_destroy_ktable.
> 
> I think it is a problem that pfr_attach_table is being called
> with 1 (intr) because the introducing commit says:
> 
> commit 4b3977248902c22d96aaebdb5784840debc2631c
> Author: mikeb 
> Date:   Mon Nov 24 13:22:09 2008 +
> 
> Fix splasserts seen in pr 5987 by propagating a flag that discribes
> whether we're called from the interrupt context to the functions
> performing allocations.
> 
> Looked at by mpf@ and henning@, tested by mpf@ and Antti Harri,
> the pr originator.
> 
> ok tedu
> 
> And we are not in interrupt context. A detailed analysis follows.

It's been a while, but iirc the flag is not necessarily whether this call,
happening right now, is interrupt or not, but whether the paired alloc or
free was or will be. So user code may need to set the intr flag if later
an interrupt context will be touching this. (Interrupts are still not ever
allowed to use user context allocators.)

It may be more complicated than just flipping the flag, because there may
be other ways to reach this free that require it. Or it could just be a bug.
Just make sure we never free anything allocated with intr in this path.



another syzkaller problem in pf

2022-05-03 Thread Moritz Buhl
Hi tech@,

Syzkaller found a few crashes in pf_anchor_global_RB_REMOVE.
https://syzkaller.appspot.com/bug?id=a97f712331903ce38b8c084a489818b9bb5c6fcb
and also
https://syzkaller.appspot.com/text?tag=CrashLog=15ace9aaf0

The call stack is something like this:
pf_anchor_global_RB_REMOVE
pf_remove_if_empty_ruleset
pfi_dynaddr_setup
pfioctl

I believe this is caused by two calls to pf_remove_if_empty_ruleset
in pfi_dynaddr_setup and a possibly pfr_destroy_ktable.

I think it is a problem that pfr_attach_table is being called
with 1 (intr) because the introducing commit says:

commit 4b3977248902c22d96aaebdb5784840debc2631c
Author: mikeb 
Date:   Mon Nov 24 13:22:09 2008 +

Fix splasserts seen in pr 5987 by propagating a flag that discribes
whether we're called from the interrupt context to the functions
performing allocations.

Looked at by mpf@ and henning@, tested by mpf@ and Antti Harri,
the pr originator.

ok tedu

And we are not in interrupt context. A detailed analysis follows.


The pfioctl cd60441a is DIOCADDRULE.

The call stack fails in pf_addr_setup calls from pf_ioctl.c:
1647 if (pf_addr_setup(ruleset, >src.addr,
1648 newrule->af))
similar calls below.


One of these calls might do the following:
424 pfi_dynaddr_setup(struct pf_addr_wrap *aw, sa_family_t af)
...
462 if ((ruleset = pf_find_or_create_ruleset(PF_RESERVED_ANCHOR)) == 
NULL) {
assume false.
...
467 if ((dyn->pfid_kt = pfr_attach_table(ruleset, tblname, 1)) == NULL)
might evaluate to true, I will share my thoughts on it.
468 rv = 1;
469 goto _bad;
...
481 _bad:
482 if (dyn->pfid_kt != NULL)
483 pfr_detach_table(dyn->pfid_kt);
484 if (ruleset != NULL)
485 pf_remove_if_empty_ruleset(ruleset);
remember this call. I will show a case where we don't want this.

due to the following in pf_table.c:
2424 pfr_attach_table(struct pf_ruleset *rs, char *name, int intr)
...
2434 kt = pfr_lookup_table();
2435 if (kt == NULL) {
assume true.
2436 kt = pfr_create_ktable(, gettime(), 1, intr);
2437 if (kt == NULL)
2438 return (NULL);
2439 if (ac != NULL) {
assume true.
this means kt != NULL and kt->pfrkt_rs = our ruleset.
2440 bzero(tbl.pfrt_anchor, sizeof(tbl.pfrt_anchor));
2441 rt = pfr_lookup_table();
2442 if (rt == NULL) {
assume true.
pfr_ktable_compares pfrkt_name an pfrkt_anchor if previous was same.
pfrkt_name should be '\0', pfrkt_anchor is PF_RESERVED_ANCHOR vs '\0'
2443 rt = pfr_create_ktable(, 0, 1, intr);
let's look into this call next just to be sure.
2444 if (rt == NULL) {
assume true.
2445 pfr_destroy_ktable(kt, 0);
remember this call is possible. We look at this afterwards.
2446 return (NULL);
...

looking into create_ktable, I want to see if we can reach pfr_destroy_ktable,
still in pf_table.c:
2202 pfr_create_ktable(struct pfr_table *tbl, time_t tzero, int attachruleset
...
2208 if (intr)
2209 kt = pool_get(_ktable_pl, 
PR_NOWAIT|PR_ZERO|PR_LIMITFAIL);
This can fail. Returning NULL afterwards.
Note PR_ZERO.
...
2216 if (attachruleset) {
2217 rs = pf_find_or_create_ruleset(tbl->pfrt_anchor);
2218 if (!rs) {
not reachable because pfrt_anchor comes from l.2433.
But a weird indirection for our current call stack anyway. Might be necessary.
2219 pfr_destroy_ktable(kt, 0);
2220 return (NULL);
2221 }
 kt->pfrkt_rs = rs;
2223 rs->tables++;
2224 }
2225
2226 if (!rn_inithead((void **)>pfrkt_ip4,
2227 offsetof(struct sockaddr_in, sin_addr)) ||
2228 !rn_inithead((void **)>pfrkt_ip6,
2229 offsetof(struct sockaddr_in6, sin6_addr))) {
unreachable because >pfrkt_ip{4,6} are NULL.
2230 pfr_destroy_ktable(kt, 0);
2231 return (NULL);
2232 }

Back to pfr_destroy_ktable, still in pf_table.c:
2253 pfr_destroy_ktable(struct pfr_ktable *kt, int flushaddr)
2254 {
...
2268 if (kt->pfrkt_rs != NULL) {
assume true.
2269 kt->pfrkt_rs->tables--;
2270 pf_remove_if_empty_ruleset(kt->pfrkt_rs);
our first call to pf_remove_if_empty_ruleset.
The second one follows from pfi_dynaddr_setup.

If this is sound, then the only reason why pfr_destroy_ktable was called
is that pool_get is called with PR_NOWAIT.  And then the following diff
would help.

mbuhl


Index: pf_if.c
===
RCS file: /cvs/src/sys/net/pf_if.c,v
retrieving revision 1.104
diff -u -p