git: fcafa2453102 - releng/14.0 - timerfd: fix up a memory leak and missing locking

From: Warner Losh <imp_at_FreeBSD.org>
Date: Thu, 28 Sep 2023 22:28:14 UTC
The branch releng/14.0 has been updated by imp:

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

commit fcafa24531021555976b4945d9c58667c0f75ca2
Author:     Mateusz Guzik <mjg@FreeBSD.org>
AuthorDate: 2023-08-25 14:21:39 +0000
Commit:     Warner Losh <imp@FreeBSD.org>
CommitDate: 2023-09-28 22:26:19 +0000

    timerfd: fix up a memory leak and missing locking
    
    timerfd01 from ltp passes (and some other don't), but none of the tests
    crash the kernel.
    
    This is a bare minimum patch to fix up the immediate regression.
    
    Reported by:    yasu
    
    (cherry picked from commit 02f534b57f84d6f4f97c337b05b383c8b3aaf18c)
    
    Approved by: re (cperciva@)
    
    (cherry picked from commit 8c496b26641e3d86b0c9db0ed2b4598093df6d5a)
    
    Approved-by: re (cperciva)
---
 sys/kern/sys_timerfd.c | 31 +++++++++++++++++++++----------
 1 file changed, 21 insertions(+), 10 deletions(-)

diff --git a/sys/kern/sys_timerfd.c b/sys/kern/sys_timerfd.c
index 6948fa059b8c..2bf2a05c443c 100644
--- a/sys/kern/sys_timerfd.c
+++ b/sys/kern/sys_timerfd.c
@@ -43,6 +43,7 @@
 #include <sys/queue.h>
 #include <sys/selinfo.h>
 #include <sys/stat.h>
+#include <sys/sx.h>
 #include <sys/sysctl.h>
 #include <sys/sysent.h>
 #include <sys/sysproto.h>
@@ -59,7 +60,11 @@
 #endif
 
 static MALLOC_DEFINE(M_TIMERFD, "timerfd", "timerfd structures");
-static LIST_HEAD(, timerfd) timerfd_head;
+
+static struct sx timerfd_list_lock;
+static LIST_HEAD(, timerfd) timerfd_list;
+SX_SYSINIT(timerfd, &timerfd_list_lock, "timerfd_list_lock");
+
 static struct unrhdr64 tfdino_unr;
 
 #define	TFD_NOJUMP	0	/* Realtime clock has not jumped. */
@@ -125,7 +130,8 @@ timerfd_jumped(void)
 	struct timespec boottime, diff;
 
 	timerfd_getboottime(&boottime);
-	LIST_FOREACH(tfd, &timerfd_head, entry) {
+	sx_xlock(&timerfd_list_lock);
+	LIST_FOREACH(tfd, &timerfd_list, entry) {
 		mtx_lock(&tfd->tfd_lock);
 		if (tfd->tfd_clockid != CLOCK_REALTIME ||
 		    (tfd->tfd_timflags & TFD_TIMER_ABSTIME) == 0 ||
@@ -160,6 +166,7 @@ timerfd_jumped(void)
 		tfd->tfd_boottim = boottime;
 		mtx_unlock(&tfd->tfd_lock);
 	}
+	sx_xunlock(&timerfd_list_lock);
 }
 
 static int
@@ -314,11 +321,14 @@ timerfd_close(struct file *fp, struct thread *td)
 {
 	struct timerfd *tfd = fp->f_data;
 
+	sx_xlock(&timerfd_list_lock);
+	LIST_REMOVE(tfd, entry);
+	sx_xunlock(&timerfd_list_lock);
+
 	callout_drain(&tfd->tfd_callout);
 	seldrain(&tfd->tfd_sel);
 	knlist_destroy(&tfd->tfd_sel.si_note);
 	mtx_destroy(&tfd->tfd_lock);
-	LIST_REMOVE(tfd, entry);
 	free(tfd, M_TIMERFD);
 	fp->f_ops = &badfileops;
 
@@ -420,9 +430,11 @@ kern_timerfd_create(struct thread *td, int clockid, int flags)
 	if ((flags & TFD_CLOEXEC) != 0)
 		fflags |= O_CLOEXEC;
 
+	error = falloc(td, &fp, &fd, fflags);
+	if (error != 0)
+		return (error);
+
 	tfd = malloc(sizeof(*tfd), M_TIMERFD, M_WAITOK | M_ZERO);
-	if (tfd == NULL)
-		return (ENOMEM);
 	tfd->tfd_clockid = (clockid_t)clockid;
 	tfd->tfd_flags = flags;
 	tfd->tfd_ino = alloc_unr64(&tfdino_unr);
@@ -431,16 +443,15 @@ kern_timerfd_create(struct thread *td, int clockid, int flags)
 	knlist_init_mtx(&tfd->tfd_sel.si_note, &tfd->tfd_lock);
 	timerfd_getboottime(&tfd->tfd_boottim);
 	getnanotime(&tfd->tfd_birthtim);
-	LIST_INSERT_HEAD(&timerfd_head, tfd, entry);
+	sx_xlock(&timerfd_list_lock);
+	LIST_INSERT_HEAD(&timerfd_list, tfd, entry);
+	sx_xunlock(&timerfd_list_lock);
 
-	error = falloc(td, &fp, &fd, fflags);
-	if (error != 0)
-		return (error);
 	fflags = FREAD;
 	if ((flags & TFD_NONBLOCK) != 0)
 		fflags |= FNONBLOCK;
-
 	finit(fp, fflags, DTYPE_TIMERFD, tfd, &timerfdops);
+
 	fdrop(fp, td);
 
 	td->td_retval[0] = fd;