git: c895c72120e2 - releng/15.1 - syslogd: reap pipe children on config reload

From: Mark Johnston <markj_at_FreeBSD.org>
Date: Tue, 29 Sep 2026 15:59:58 UTC
The branch releng/15.1 has been updated by markj:

URL: https://cgit.FreeBSD.org/src/commit/?id=c895c72120e24c69447676d20d35df9544d2d89e

commit c895c72120e24c69447676d20d35df9544d2d89e
Author:     Baptiste Daroussin <bapt@FreeBSD.org>
AuthorDate: 2026-09-02 08:51:42 +0000
Commit:     Mark Johnston <markj@FreeBSD.org>
CommitDate: 2026-09-28 13:10:19 +0000

    syslogd: reap pipe children on config reload
    
    On SIGHUP reload, closelogfiles() frees each F_PIPE filed even when its
    pipe process is still running.  close_filed() sets f_type to F_UNUSED
    before the check, so the condition f_type != F_PIPE is always true and
    the filed is freed while its process descriptor is still on the dead
    queue and registered in the kqueue.  When the child later exits, the
    NOTE_EXIT handler dereferences the freed filed (use-after-free) and
    never closes the process descriptor, leaving the pipe child as a
    persistent zombie.
    
    Capture whether the filed is a pipe with an active process descriptor
    before calling close_filed(), and defer the free in that case so the
    NOTE_EXIT handler can reap the child and free the filed.
    
    Approved by:    so
    Security:       FreeBSD-EN-26:23.syslogd
    Reviewed by:    markj
    Fixes:  95381c0139d6 (syslogd: Use process descriptors)
    Differential Revision:  https://reviews.freebsd.org/D59319
    
    (cherry picked from commit 1a669b66ddb4748c24116e32dcb51eabaf4859ed)
    (cherry picked from commit 8cf005884326fd15324bc484867c0a947607cb45)
---
 usr.sbin/syslogd/syslogd.c | 14 +++++++++-----
 1 file changed, 9 insertions(+), 5 deletions(-)

diff --git a/usr.sbin/syslogd/syslogd.c b/usr.sbin/syslogd/syslogd.c
index ee15fa70807d..515441ea2411 100644
--- a/usr.sbin/syslogd/syslogd.c
+++ b/usr.sbin/syslogd/syslogd.c
@@ -2495,6 +2495,7 @@ void
 closelogfiles(void)
 {
 	struct filed *f;
+	bool defer_free;
 
 	while (!STAILQ_EMPTY(&fhead)) {
 		f = STAILQ_FIRST(&fhead);
@@ -2504,6 +2505,13 @@ closelogfiles(void)
 		if (f->f_prevcount)
 			fprintlog_successive(f, 0);
 
+		/*
+		 * If a piped process is running, then defer the filed
+		 * cleanup until it exits.  close_filed() below sets
+		 * f_type to F_UNUSED, so capture this before calling it.
+		 */
+		defer_free = (f->f_type == F_PIPE && f->f_procdesc != -1);
+
 		switch (f->f_type) {
 		case F_FILE:
 		case F_FORW:
@@ -2531,11 +2539,7 @@ closelogfiles(void)
 			free(f->f_prop_filter);
 		}
 
-		/*
-		 * If a piped process is running, then defer the filed
-		 * cleanup until it exits.
-		 */
-		if (f->f_type != F_PIPE || f->f_procdesc == -1)
+		if (!defer_free)
 			free(f);
 	}
 }