From: CypherFox Subject: FIX: memory leak in relayd(8) config_purge To: tech@openbsd.org Date: Tue, 31 Mar 2026 01:01:18 +0200 Hello, I found a memory leak in relayd's config_purge() when handling CONFIG_PROTOS. The current implementation uses two separate loops: the first one empties the sc_protos list, which causes the second loop (responsible for the actual free() calls) to be skipped entirely. I've verified this with lldb. The second loop is never entered because TAILQ_FIRST(env->sc_protos) returns NULL after the first loop finishes, leaving the protocol structures and their members orphaned in the heap: (lldb) p *env->sc_protos (protolist) { tqh_first = NULL tqh_last = 0x000007fcaf00ee30 } (lldb) p *env->sc_protos->tqh_last (protocol *) NULL (lldb) p ((struct protocol *)0x000007fc3db03000)->tlscapass (char *) 0x000007fcaf003360 "s3cr3t" This patch unifies the logic into a single loop to ensure all resources (rules, tlscerts, styles, and the protocol structure) are properly freed. Index: usr.sbin/relayd/config.c =================================================================== RCS file: /cvs/src/usr.sbin/relayd/config.c,v diff -u -p -u -p -r1.48 config.c --- usr.sbin/relayd/config.c 2 Mar 2026 19:28:01 -0000 1.48 +++ usr.sbin/relayd/config.c 30 Mar 2026 21:47:03 -0000 @@ -186,19 +186,13 @@ config_purge(struct relayd *env, u_int r while ((rule = TAILQ_FIRST(&proto->rules)) != NULL) rule_delete(&proto->rules, rule); proto->rulecount = 0; - } - } - if (what & CONFIG_PROTOS && env->sc_protos != NULL) { - while ((proto = TAILQ_FIRST(env->sc_protos)) != NULL) { - TAILQ_REMOVE(env->sc_protos, proto, entry); - free(proto->style); - free(proto->tlscapass); - while ((keyname = - TAILQ_FIRST(&proto->tlscerts)) != NULL) { + while ((keyname = TAILQ_FIRST(&proto->tlscerts)) != NULL) { TAILQ_REMOVE(&proto->tlscerts, keyname, entry); free(keyname->name); free(keyname); } + free(proto->style); + free(proto->tlscapass); free(proto); } env->sc_protocount = 0;