git: 0e8f351be793 - main - timerfd: Use saturating sbintime conversions

From: Jake Freeland <jfree_at_FreeBSD.org>
Date: Fri, 20 Mar 2026 07:16:16 UTC
The branch main has been updated by jfree:

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

commit 0e8f351be7935729bf67dc1b7aa4d178cf154931
Author:     Jake Freeland <jfree@FreeBSD.org>
AuthorDate: 2026-03-20 06:33:38 +0000
Commit:     Jake Freeland <jfree@FreeBSD.org>
CommitDate: 2026-03-20 07:15:08 +0000

    timerfd: Use saturating sbintime conversions
    
    Some timerfd consumers set expirations with timespec tv_sec components
    larger than 2^31 - 1. In such cases, converting that timespec to
    sbintime results in data loss or sign flip, yielding a shorter
    expiration than desired.
    
    To avoid this problem, use saturating timespec-to-sbintime conversion
    functions. These will clamp the converted sbintime to SBT_MAX under
    circumstances where the normal conversion functions would overflow.
    
    Saturating conversions still result in data loss, but the consequences
    are less severe, causing problems only after SBT_MAX (~68 years) of
    system uptime elapses.
    
    Reviewed by:            imp
    Differential Revision:  https://reviews.freebsd.org/D55792
    MFC after:              2 weeks
---
 sys/kern/sys_timerfd.c | 10 +++++-----
 1 file changed, 5 insertions(+), 5 deletions(-)

diff --git a/sys/kern/sys_timerfd.c b/sys/kern/sys_timerfd.c
index 236dfe8bb96a..f2a66e21cd63 100644
--- a/sys/kern/sys_timerfd.c
+++ b/sys/kern/sys_timerfd.c
@@ -165,7 +165,7 @@ timerfd_jumped(void)
 				    &diff, &tfd->tfd_time.it_value);
 				if (callout_stop(&tfd->tfd_callout) == 1) {
 					callout_schedule_sbt(&tfd->tfd_callout,
-					    tstosbt(tfd->tfd_time.it_value),
+					    tstosbt_sat(tfd->tfd_time.it_value),
 					    0, C_ABSOLUTE);
 				}
 			}
@@ -398,10 +398,10 @@ timerfd_expire(void *arg)
 	++tfd->tfd_count;
 	tfd->tfd_expired = true;
 	if (timespecisset(&tfd->tfd_time.it_interval)) {
-		exp = tstosbt(tfd->tfd_time.it_value);
-		interval = tstosbt(tfd->tfd_time.it_interval);
+		exp = tstosbt_sat(tfd->tfd_time.it_value);
+		interval = tstosbt_sat(tfd->tfd_time.it_interval);
 		now = sbinuptime();
-		next = now + interval;
+		next = now > SBT_MAX - interval ? SBT_MAX : now + interval;
 
 		/* Count missed events. */
 		if (now > exp) {
@@ -553,7 +553,7 @@ kern_timerfd_settime(struct thread *td, int fd, int flags,
 			    &tfd->tfd_time.it_value);
 		}
 		callout_reset_sbt(&tfd->tfd_callout,
-		    tstosbt(tfd->tfd_time.it_value),
+		    tstosbt_sat(tfd->tfd_time.it_value),
 		    0, timerfd_expire, tfd, C_ABSOLUTE);
 	} else {
 		callout_stop(&tfd->tfd_callout);