git: eeb0682964fc - stable/13 - posix timers: Improve the overrun calculation

Mark Johnston markj at FreeBSD.org
Mon Mar 15 15:41:33 UTC 2021


The branch stable/13 has been updated by markj:

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

commit eeb0682964fccae585ce61ee322dd0062b7a4cd6
Author:     Mark Johnston <markj at FreeBSD.org>
AuthorDate: 2021-03-08 17:39:06 +0000
Commit:     Mark Johnston <markj at FreeBSD.org>
CommitDate: 2021-03-15 15:38:43 +0000

    posix timers: Improve the overrun calculation
    
    timer_settime(2) may be used to configure a timeout in the past.  If
    the timer is also periodic, we also try to compute the number of timer
    overruns that occurred between the initial timeout and the time at which
    the timer fired.  This is done in a loop which iterates once per period
    between the initial timeout and now.  If the period is small and the
    initial timeout was a long time ago, this loop can take forever to run,
    so the system is effectively DOSed.
    
    Replace the loop with a more direct calculation of
    (now - initial timeout) / period to compute the number of overruns.
    
    Reported by:    syzkaller
    Reviewed by:    kib
    Sponsored by:   The FreeBSD Foundation
    Differential Revision:  https://reviews.freebsd.org/D29093
    
    (cherry picked from commit 7995dae9d3f58abf38ef0001cee24131f3c9054b)
---
 sys/kern/kern_time.c | 35 ++++++++++++++++++++++++++++-------
 1 file changed, 28 insertions(+), 7 deletions(-)

diff --git a/sys/kern/kern_time.c b/sys/kern/kern_time.c
index 3e85f8e1d6ec..44f6b4ad07f2 100644
--- a/sys/kern/kern_time.c
+++ b/sys/kern/kern_time.c
@@ -1603,6 +1603,13 @@ itimespecfix(struct timespec *ts)
 	return (0);
 }
 
+#define	timespectons(tsp)			\
+	((uint64_t)(tsp)->tv_sec * 1000000000 + (tsp)->tv_nsec)
+#define	timespecfromns(ns) (struct timespec){	\
+	.tv_sec = (ns) / 1000000000,		\
+	.tv_nsec = (ns) % 1000000000		\
+}
+
 /* Timeout callback for realtime timer */
 static void
 realtimer_expire(void *arg)
@@ -1610,6 +1617,7 @@ realtimer_expire(void *arg)
 	struct timespec cts, ts;
 	struct timeval tv;
 	struct itimer *it;
+	uint64_t interval, now, overruns, value;
 
 	it = (struct itimer *)arg;
 
@@ -1620,14 +1628,27 @@ realtimer_expire(void *arg)
 			timespecadd(&it->it_time.it_value,
 			    &it->it_time.it_interval,
 			    &it->it_time.it_value);
-			while (timespeccmp(&cts, &it->it_time.it_value, >=)) {
-				if (it->it_overrun < INT_MAX)
-					it->it_overrun++;
-				else
+
+			interval = timespectons(&it->it_time.it_interval);
+			value = timespectons(&it->it_time.it_value);
+			now = timespectons(&cts);
+
+			if (now >= value) {
+				/*
+				 * We missed at least one period.
+				 */
+				overruns = howmany(now - value + 1, interval);
+				if (it->it_overrun + overruns >=
+				    it->it_overrun &&
+				    it->it_overrun + overruns <= INT_MAX) {
+					it->it_overrun += (int)overruns;
+				} else {
+					it->it_overrun = INT_MAX;
 					it->it_ksi.ksi_errno = ERANGE;
-				timespecadd(&it->it_time.it_value,
-				    &it->it_time.it_interval,
-				    &it->it_time.it_value);
+				}
+				value =
+				    now + interval - (now - value) % interval;
+				it->it_time.it_value = timespecfromns(value);
 			}
 		} else {
 			/* single shot timer ? */


More information about the dev-commits-src-all mailing list