Index | Thread | Search

From:
Lexi Wilson <lexi@cooperi.net>
Subject:
fq_codel devbuf leak on pf reload
To:
tech@openbsd.org
Date:
Wed, 4 Mar 2026 10:32:47 +1000

Download raw body.

Thread
  • Lexi Wilson:

    fq_codel devbuf leak on pf reload

Hi,

I've been chasing down a leak of M_DEVBUF malloc memory which seems to 
occur on one of my systems whenever I have a fq_codel queue in pf.conf:

matilda$ doas pfctl -f /etc/pf.conf ;systat malloc | grep ^devbuf
devbuf          34560 39024K 39097K   218M    35714     0
matilda$ doas pfctl -f /etc/pf.conf ;systat malloc | grep ^devbuf
devbuf          34562 39096K 39169K   218M    35728     0
matilda$ doas pfctl -f /etc/pf.conf ;systat malloc | grep ^devbuf
devbuf          34564 39169K 39242K   218M    35742     0
matilda$ doas pfctl -f /etc/pf.conf ;systat malloc | grep ^devbuf
devbuf          34566 39241K 39314K   218M    35756     0
matilda$ doas pfctl -f /etc/pf.conf ;systat malloc | grep ^devbuf
devbuf          34568 39313K 39386K   218M    35770     0
matilda$ doas pfctl -f /etc/pf.conf ;systat malloc | grep ^devbuf
devbuf          34570 39385K 39458K   218M    35784     0

If I run `pfctl -f` enough this eventually exhausts devbuf and deadlocks 
this machine, with pfctl holding netlock waiting for devbuf, ISC named 
holding sysctllk waiting for netlock, and everything else piled up 
behind sysctllk (which is how I noticed it in the first place, since 
this particular firewall runs pfctl periodically after interface changes 
and was hitting the deadlock every few weeks)

After some reading of fq_codel.c I noticed that the ifq_free callback 
for fq_codel (fqcodel_free) does nothing, even though there is allocated 
memory hanging off the struct fqcodel, as well as the struct fqcodel 
itself which should be freed (which you can see being deallocated in the 
*pfq* free callback, fqcodel_pf_free).

The way I understand the code in pf_ioctl.c, the PF callbacks for discs 
are never called once they actually get put on an interface: only the 
ifq_free gets called past that point, so it needs to free the memory the 
same way as fqcodel_pf_free does.

HFSC by contrast frees everything in hfsc_free, and makes hfsc_pf_free 
call that. (And, indeed, if I change my queues to HFSC, no leak.)

I applied the following patch on my machine and it seems to resolve the 
problem:

--- a/sys/net/fq_codel.c
+++ b/sys/net/fq_codel.c
@@ -908,5 +908,5 @@ fqcodel_alloc(unsigned int idx, void *arg)
  void
  fqcodel_free(unsigned int idx, void *arg)
  {
-       /* nothing to do here */
+       fqcodel_pf_free(arg);
  }

Feels like an easy one to me, unless I'm missing something? (Which I 
could definitely be, I'm still very new to the PF code)