public inbox for linux-kernel@vger.kernel.org
 help / color / mirror / Atom feed
From: Wake Liu <wakel@google.com>
To: Anna-Maria Behnsen <anna-maria@linutronix.de>,
	Frederic Weisbecker <frederic@kernel.org>,
	 Thomas Gleixner <tglx@linutronix.de>,
	John Stultz <jstultz@google.com>, Shuah Khan <shuah@kernel.org>,
	 linux-kernel@vger.kernel.org, linux-kselftest@vger.kernel.org
Cc: Stephen Boyd <sboyd@kernel.org>, wakel@google.com
Subject: [PATCH v2] selftests/timers: Consolidate and fix 32-bit overflow in timespec_sub
Date: Tue, 16 Sep 2025 03:19:44 +0800	[thread overview]
Message-ID: <20250915191944.9779-1-wakel@google.com> (raw)
In-Reply-To: <fbb55063-ab03-40a9-80f4-4315d12239ba@t-8ch.de>

The timespec_sub function, as implemented in several timer
selftests, is prone to integer overflow on 32-bit systems.

The calculation `NSEC_PER_SEC * b.tv_sec` is performed using
32-bit arithmetic, and the result overflows before being
stored in the 64-bit `ret` variable. This leads to incorrect
time delta calculations and test failures.

As suggested by tglx, this patch fixes the issue by:

1. Creating a new `static inline` helper function,
   `timespec_to_ns`, which safely converts a `timespec` to
   nanoseconds by casting `tv_sec` to `long long` before
   multiplying with `NSEC_PER_SEC`.

2. Placing the new helper and a rewritten `timespec_sub` into
   a common header: tools/testing/selftests/timers/helpers.h.

3. Removing the duplicated, buggy implementations from all
   timer selftests and replacing them with an #include of the
   new header.

This consolidates the code and ensures the calculation is
correctly performed using 64-bit arithmetic across all tests.

Changes in v2:
  - Per tglx's feedback, instead of changing NSEC_PER_SEC globally,
    this version consolidates the buggy timespec_sub() implementations
    into a new 32-bit safe inline function in a shared header.
  - Amended the commit message to be more descriptive.
---
 .../selftests/timers/alarmtimer-suspend.c     | 15 +++------
 tools/testing/selftests/timers/helpers.h      | 31 +++++++++++++++++++
 tools/testing/selftests/timers/nanosleep.c    |  2 +-
 tools/testing/selftests/timers/nsleep-lat.c   | 12 ++-----
 .../testing/selftests/timers/valid-adjtimex.c |  8 ++---
 5 files changed, 43 insertions(+), 25 deletions(-)
 create mode 100644 tools/testing/selftests/timers/helpers.h

diff --git a/tools/testing/selftests/timers/alarmtimer-suspend.c b/tools/testing/selftests/timers/alarmtimer-suspend.c
index a9ef76ea6051..e85ab182abe5 100644
--- a/tools/testing/selftests/timers/alarmtimer-suspend.c
+++ b/tools/testing/selftests/timers/alarmtimer-suspend.c
@@ -31,8 +31,9 @@
 #include <include/vdso/time64.h>
 #include <errno.h>
 #include "../kselftest.h"
+#include "helpers.h"
 
-#define UNREASONABLE_LAT (NSEC_PER_SEC * 5) /* hopefully we resume in 5 secs */
+#define UNREASONABLE_LAT (NSEC_PER_SEC * 5LL) /* hopefully we resume in 5 secs */
 
 #define SUSPEND_SECS 15
 int alarmcount;
@@ -70,14 +71,6 @@ char *clockstring(int clockid)
 }
 
 
-long long timespec_sub(struct timespec a, struct timespec b)
-{
-	long long ret = NSEC_PER_SEC * b.tv_sec + b.tv_nsec;
-
-	ret -= NSEC_PER_SEC * a.tv_sec + a.tv_nsec;
-	return ret;
-}
-
 int final_ret;
 
 void sigalarm(int signo)
@@ -88,8 +81,8 @@ void sigalarm(int signo)
 	clock_gettime(alarm_clock_id, &ts);
 	alarmcount++;
 
-	delta_ns = timespec_sub(start_time, ts);
-	delta_ns -= NSEC_PER_SEC * SUSPEND_SECS * alarmcount;
+	delta_ns = timespec_sub(ts, start_time);
+	delta_ns -= (long long)NSEC_PER_SEC * SUSPEND_SECS * alarmcount;
 
 	printf("ALARM(%i): %ld:%ld latency: %lld ns ", alarmcount, ts.tv_sec,
 							ts.tv_nsec, delta_ns);
diff --git a/tools/testing/selftests/timers/helpers.h b/tools/testing/selftests/timers/helpers.h
new file mode 100644
index 000000000000..652f20247091
--- /dev/null
+++ b/tools/testing/selftests/timers/helpers.h
@@ -0,0 +1,31 @@
+#ifndef SELFTESTS_TIMERS_HELPERS_H
+#define SELFTESTS_TIMERS_HELPERS_H
+
+#include <time.h>
+
+#ifndef NSEC_PER_SEC
+#define NSEC_PER_SEC 1000000000L
+#endif
+
+/*
+ * timespec_to_ns - Convert timespec to nanoseconds
+ */
+static inline long long timespec_to_ns(const struct timespec *ts)
+{
+	return ((long long) ts->tv_sec * NSEC_PER_SEC) + ts->tv_nsec;
+}
+
+/*
+ * timespec_sub - Subtract two timespec values
+ *
+ * @a: first timespec
+ * @b: second timespec
+ *
+ * Returns a - b in nanoseconds.
+ */
+static inline long long timespec_sub(struct timespec a, struct timespec b)
+{
+	return timespec_to_ns(&a) - timespec_to_ns(&b);
+}
+
+#endif /* SELFTESTS_TIMERS_HELPERS_H */
diff --git a/tools/testing/selftests/timers/nanosleep.c b/tools/testing/selftests/timers/nanosleep.c
index 252c6308c569..41c33629d5f0 100644
--- a/tools/testing/selftests/timers/nanosleep.c
+++ b/tools/testing/selftests/timers/nanosleep.c
@@ -138,7 +138,7 @@ int main(int argc, char **argv)
 		fflush(stdout);
 
 		length = 10;
-		while (length <= (NSEC_PER_SEC * 10)) {
+		while (length <= (NSEC_PER_SEC * 10LL)) {
 			ret = nanosleep_test(clockid, length);
 			if (ret == UNSUPPORTED) {
 				ksft_test_result_skip("%-31s\n", clockstring(clockid));
diff --git a/tools/testing/selftests/timers/nsleep-lat.c b/tools/testing/selftests/timers/nsleep-lat.c
index de23dc0c9f97..c888a77aab7a 100644
--- a/tools/testing/selftests/timers/nsleep-lat.c
+++ b/tools/testing/selftests/timers/nsleep-lat.c
@@ -26,6 +26,7 @@
 #include <signal.h>
 #include <include/vdso/time64.h>
 #include "../kselftest.h"
+#include "helpers.h"
 
 #define UNRESONABLE_LATENCY 40000000 /* 40ms in nanosecs */
 
@@ -74,14 +75,6 @@ struct timespec timespec_add(struct timespec ts, unsigned long long ns)
 }
 
 
-long long timespec_sub(struct timespec a, struct timespec b)
-{
-	long long ret = NSEC_PER_SEC * b.tv_sec + b.tv_nsec;
-
-	ret -= NSEC_PER_SEC * a.tv_sec + a.tv_nsec;
-	return ret;
-}
-
 int nanosleep_lat_test(int clockid, long long ns)
 {
 	struct timespec start, end, target;
@@ -146,7 +139,7 @@ int main(int argc, char **argv)
 			continue;
 
 		length = 10;
-		while (length <= (NSEC_PER_SEC * 10)) {
+		while (length <= (NSEC_PER_SEC * 10LL)) {
 			ret = nanosleep_lat_test(clockid, length);
 			if (ret)
 				break;
@@ -164,3 +157,4 @@ int main(int argc, char **argv)
 
 	ksft_finished();
 }
+
diff --git a/tools/testing/selftests/timers/valid-adjtimex.c b/tools/testing/selftests/timers/valid-adjtimex.c
index 6b7801055ad1..a61d4b4739a2 100644
--- a/tools/testing/selftests/timers/valid-adjtimex.c
+++ b/tools/testing/selftests/timers/valid-adjtimex.c
@@ -260,16 +260,16 @@ int validate_set_offset(void)
 	if (set_offset(-NSEC_PER_SEC - 1, 1))
 		return -1;
 
-	if (set_offset(5 * NSEC_PER_SEC, 1))
+	if (set_offset(5LL * NSEC_PER_SEC, 1))
 		return -1;
 
-	if (set_offset(-5 * NSEC_PER_SEC, 1))
+	if (set_offset(-5LL * NSEC_PER_SEC, 1))
 		return -1;
 
-	if (set_offset(5 * NSEC_PER_SEC + NSEC_PER_SEC / 2, 1))
+	if (set_offset(5LL * NSEC_PER_SEC + NSEC_PER_SEC / 2, 1))
 		return -1;
 
-	if (set_offset(-5 * NSEC_PER_SEC - NSEC_PER_SEC / 2, 1))
+	if (set_offset(-5LL * NSEC_PER_SEC - NSEC_PER_SEC / 2, 1))
 		return -1;
 
 	if (set_offset(USEC_PER_SEC - 1, 0))
-- 
2.51.0.384.g4c02a37b29-goog


  reply	other threads:[~2025-09-15 19:19 UTC|newest]

Thread overview: 11+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2025-08-05 16:21 [PATCH] vdso: Define NSEC_PER_SEC as 64-bit to prevent overflow Wake Liu
2025-08-06  8:44 ` Thomas Gleixner
2025-08-06  8:55 ` Thomas Gleixner
2025-08-09  9:49   ` Wake Liu
2025-08-10  8:03     ` Thomas Gleixner
2025-08-10  8:28       ` Thomas Weißschuh
2025-09-15 19:19         ` Wake Liu [this message]
2025-09-21  7:49           ` [PATCH v2] selftests/timers: Consolidate and fix 32-bit overflow in timespec_sub Thomas Gleixner
2026-01-07 23:19             ` Carlos Llamas
2025-08-06 20:28 ` [PATCH] vdso: Define NSEC_PER_SEC as 64-bit to prevent overflow kernel test robot
2025-08-07  7:37 ` kernel test robot

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20250915191944.9779-1-wakel@google.com \
    --to=wakel@google.com \
    --cc=anna-maria@linutronix.de \
    --cc=frederic@kernel.org \
    --cc=jstultz@google.com \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-kselftest@vger.kernel.org \
    --cc=sboyd@kernel.org \
    --cc=shuah@kernel.org \
    --cc=tglx@linutronix.de \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox