From: Lexi Wilson Subject: fq_codel devbuf leak on pf reload To: tech@openbsd.org Date: Wed, 4 Mar 2026 10:32:47 +1000 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)