public inbox for ltp@lists.linux.it
 help / color / mirror / Atom feed
From: Andrea Cervesato <andrea.cervesato@suse.de>
To: Linux Test Project <ltp@lists.linux.it>
Subject: [LTP] [PATCH] clock_settime: Detect external clock adjustments via CLOCK_MONOTONIC
Date: Tue, 31 Mar 2026 14:11:44 +0200	[thread overview]
Message-ID: <20260331-clock_settime_fix-v1-1-dfae06df2436@suse.com> (raw)

From: Andrea Cervesato <andrea.cervesato@suse.com>

These tests manipulate CLOCK_REALTIME to verify timer and
clock_nanosleep behavior, but NTP or VM time sync can also adjust
CLOCK_REALTIME during the test, causing timers to fire at wrong times
and producing spurious failures.

Use CLOCK_MONOTONIC as a sideband check to detect external interference.
If the monotonic elapsed time is anomalous, report UNTESTED instead of
a false FAIL.

Signed-off-by: Andrea Cervesato <andrea.cervesato@suse.com>
---
I used Claude to generate the right patches to fix sporadic issues in the
clock_settime testing suite. We tried many attempt for fixing it, but the
CLOCK_REALTIME usage seems to be the real issue in here. The idea is that
we use internal Openposix mechanisms to skip tests if CLOCK_MONOTONIC
usage spots a possible interference from external tools.
---
 .../conformance/interfaces/clock_settime/4-1.c     | 26 +++++++++++++++++
 .../conformance/interfaces/clock_settime/5-1.c     | 33 +++++++++++++++++++++-
 .../conformance/interfaces/clock_settime/5-2.c     | 33 +++++++++++++++++++++-
 .../conformance/interfaces/clock_settime/7-1.c     | 28 ++++++++++++++++++
 .../conformance/interfaces/clock_settime/7-2.c     | 31 +++++++++++++++++++-
 5 files changed, 148 insertions(+), 3 deletions(-)

diff --git a/testcases/open_posix_testsuite/conformance/interfaces/clock_settime/4-1.c b/testcases/open_posix_testsuite/conformance/interfaces/clock_settime/4-1.c
index f8a3e9c542ca8c9767cdd0057005e519f58b2b55..005d440985a71f62ede60b7696f6bd6f8d48f4b9 100644
--- a/testcases/open_posix_testsuite/conformance/interfaces/clock_settime/4-1.c
+++ b/testcases/open_posix_testsuite/conformance/interfaces/clock_settime/4-1.c
@@ -42,6 +42,7 @@ int main(void)
 {
 	struct sigevent ev;
 	struct timespec tpT0, tpT2, tpreset;
+	struct timespec mono_before, mono_after;
 	struct itimerspec its;
 	timer_t tid;
 	int delta;
@@ -82,6 +83,11 @@ int main(void)
 		return PTS_UNRESOLVED;
 	}
 
+	if (clock_gettime(CLOCK_MONOTONIC, &mono_before) != 0) {
+		perror("clock_gettime() was not successful\n");
+		return PTS_UNRESOLVED;
+	}
+
 	if (timer_create(CLOCK_REALTIME, &ev, &tid) != 0) {
 		perror("timer_create() did not return success\n");
 		return PTS_UNRESOLVED;
@@ -116,10 +122,30 @@ int main(void)
 
 	delta = tpT2.tv_sec - its.it_value.tv_sec;
 
+	if (clock_gettime(CLOCK_MONOTONIC, &mono_after) != 0) {
+		perror("clock_gettime() was not successful\n");
+		return PTS_UNRESOLVED;
+	}
+
 	// add back time waited to reset value and reset time
 	tpreset.tv_sec += tpT2.tv_sec - tpT0.tv_sec;
 	setBackTime(tpreset);
 
+	/*
+	 * The expected monotonic elapsed time is SLEEPTIME + TIMEROFFSET
+	 * since after resetting the clock, the timer must wait another
+	 * full TIMEROFFSET. If monotonic time is much shorter, an external
+	 * clock adjustment (NTP, VM sync) interfered with the test.
+	 */
+	if (mono_after.tv_sec - mono_before.tv_sec <
+	    SLEEPTIME + TIMEROFFSET - ACCEPTABLEDELTA) {
+		printf("UNTESTED: external clock adjustment detected "
+		       "(monotonic elapsed %ds, expected ~%ds)\n",
+		       (int)(mono_after.tv_sec - mono_before.tv_sec),
+		       SLEEPTIME + TIMEROFFSET);
+		return PTS_UNTESTED;
+	}
+
 	printf("delta: %d\n", delta);
 	if ((delta <= ACCEPTABLEDELTA) && (delta >= 0)) {
 		printf("Test PASSED\n");
diff --git a/testcases/open_posix_testsuite/conformance/interfaces/clock_settime/5-1.c b/testcases/open_posix_testsuite/conformance/interfaces/clock_settime/5-1.c
index 75fa591e014601b2ef9308a118992a704365392b..1d450c5f9feef0bafcfbb1b847bec33cb3e49a1f 100644
--- a/testcases/open_posix_testsuite/conformance/interfaces/clock_settime/5-1.c
+++ b/testcases/open_posix_testsuite/conformance/interfaces/clock_settime/5-1.c
@@ -46,9 +46,11 @@ int main(void)
 	struct sigevent ev;
 	struct sigaction act;
 	struct timespec tsclock, ts, tsleft, tsreset;
+	struct timespec mono_before, mono_after;
 	struct itimerspec its;
 	timer_t tid;
 	sigset_t set;
+	int ns_ret;
 
 	/* Check that we're root...can't call clock_settime with CLOCK_REALTIME otherwise */
 	if (getuid() != 0) {
@@ -97,6 +99,11 @@ int main(void)
 		return PTS_UNRESOLVED;
 	}
 
+	if (clock_gettime(CLOCK_MONOTONIC, &mono_before) != 0) {
+		perror("clock_gettime() was not successful\n");
+		return PTS_UNRESOLVED;
+	}
+
 	if (clock_gettime(CLOCK_REALTIME, &tsclock) != 0) {
 		printf("clock_gettime() did not return success\n");
 		return PTS_UNRESOLVED;
@@ -112,7 +119,31 @@ int main(void)
 	ts.tv_sec = TIMERSEC + SLEEPDELTA;
 	ts.tv_nsec = 0;
 
-	if (nanosleep(&ts, &tsleft) != -1) {
+	ns_ret = nanosleep(&ts, &tsleft);
+
+	if (clock_gettime(CLOCK_MONOTONIC, &mono_after) != 0) {
+		perror("clock_gettime() was not successful\n");
+		return PTS_UNRESOLVED;
+	}
+
+	/*
+	 * The relative timer should fire after TIMERSEC regardless of
+	 * clock_settime changes. If monotonic elapsed time is too far
+	 * from TIMERSEC, an external clock adjustment (NTP, VM sync)
+	 * interfered with the test.
+	 */
+	if (labs(mono_after.tv_sec - mono_before.tv_sec - TIMERSEC) >
+	    ACCEPTABLEDELTA + 1) {
+		printf("UNTESTED: external clock adjustment detected "
+		       "(monotonic elapsed %ds, expected ~%ds)\n",
+		       (int)(mono_after.tv_sec - mono_before.tv_sec),
+		       TIMERSEC);
+		tsreset.tv_sec += mono_after.tv_sec - mono_before.tv_sec;
+		setBackTime(tsreset);
+		return PTS_UNTESTED;
+	}
+
+	if (ns_ret != -1) {
 		printf("nanosleep() not interrupted\n");
 		return PTS_FAIL;
 	}
diff --git a/testcases/open_posix_testsuite/conformance/interfaces/clock_settime/5-2.c b/testcases/open_posix_testsuite/conformance/interfaces/clock_settime/5-2.c
index b8a60028c6d3d1a3c0d75922f39697b57329c440..67f90f2fb7d1d841c914614d57bccab1fe7d3537 100644
--- a/testcases/open_posix_testsuite/conformance/interfaces/clock_settime/5-2.c
+++ b/testcases/open_posix_testsuite/conformance/interfaces/clock_settime/5-2.c
@@ -46,9 +46,11 @@ int main(void)
 	struct sigevent ev;
 	struct sigaction act;
 	struct timespec tsclock, ts, tsleft, tsreset;
+	struct timespec mono_before, mono_after;
 	struct itimerspec its;
 	timer_t tid;
 	sigset_t set;
+	int ns_ret;
 
 	/* Check that we're root...can't call clock_settime with CLOCK_REALTIME otherwise */
 	if (getuid() != 0) {
@@ -97,6 +99,11 @@ int main(void)
 		return PTS_UNRESOLVED;
 	}
 
+	if (clock_gettime(CLOCK_MONOTONIC, &mono_before) != 0) {
+		perror("clock_gettime() was not successful\n");
+		return PTS_UNRESOLVED;
+	}
+
 	if (clock_gettime(CLOCK_REALTIME, &tsclock) != 0) {
 		printf("clock_gettime() did not return success\n");
 		return PTS_UNRESOLVED;
@@ -112,7 +119,31 @@ int main(void)
 	ts.tv_sec = TIMERSEC + SLEEPDELTA;
 	ts.tv_nsec = 0;
 
-	if (nanosleep(&ts, &tsleft) != -1) {
+	ns_ret = nanosleep(&ts, &tsleft);
+
+	if (clock_gettime(CLOCK_MONOTONIC, &mono_after) != 0) {
+		perror("clock_gettime() was not successful\n");
+		return PTS_UNRESOLVED;
+	}
+
+	/*
+	 * The relative timer should fire after TIMERSEC regardless of
+	 * clock_settime changes. If monotonic elapsed time is too far
+	 * from TIMERSEC, an external clock adjustment (NTP, VM sync)
+	 * interfered with the test.
+	 */
+	if (labs(mono_after.tv_sec - mono_before.tv_sec - TIMERSEC) >
+	    ACCEPTABLEDELTA + 1) {
+		printf("UNTESTED: external clock adjustment detected "
+		       "(monotonic elapsed %ds, expected ~%ds)\n",
+		       (int)(mono_after.tv_sec - mono_before.tv_sec),
+		       TIMERSEC);
+		tsreset.tv_sec += mono_after.tv_sec - mono_before.tv_sec;
+		setBackTime(tsreset);
+		return PTS_UNTESTED;
+	}
+
+	if (ns_ret != -1) {
 		printf("nanosleep() not interrupted\n");
 		return PTS_FAIL;
 	}
diff --git a/testcases/open_posix_testsuite/conformance/interfaces/clock_settime/7-1.c b/testcases/open_posix_testsuite/conformance/interfaces/clock_settime/7-1.c
index 569eae904ef2eff41441029d3427313107febe53..ca325abb133195dad75f957f599a936a049bf657 100644
--- a/testcases/open_posix_testsuite/conformance/interfaces/clock_settime/7-1.c
+++ b/testcases/open_posix_testsuite/conformance/interfaces/clock_settime/7-1.c
@@ -36,6 +36,7 @@
 int main(void)
 {
 	struct timespec tsT0, tsT1;
+	struct timespec mono_before, mono_after;
 	int pid;
 
 	/* Check that we're root...can't call clock_settime with CLOCK_REALTIME otherwise */
@@ -49,6 +50,11 @@ int main(void)
 		return PTS_UNRESOLVED;
 	}
 
+	if (clock_gettime(CLOCK_MONOTONIC, &mono_before) != 0) {
+		perror("clock_gettime() was not successful\n");
+		return PTS_UNRESOLVED;
+	}
+
 	if ((pid = fork()) == 0) {
 		/* child here */
 		int flags = 0;
@@ -100,9 +106,31 @@ int main(void)
 			return PTS_UNRESOLVED;
 		}
 
+		if (clock_gettime(CLOCK_MONOTONIC, &mono_after) != 0) {
+			perror("clock_gettime() was not successful\n");
+			return PTS_UNRESOLVED;
+		}
+
 		getBeforeTime(&tsreset);	// get current time
 		tsreset.tv_sec += SMALLTIME;
 		setBackTime(tsreset);
+
+		/*
+		 * Expected monotonic elapsed time is SMALLTIME + SLEEPOFFSET
+		 * since after resetting the clock, the child must wait
+		 * another full SLEEPOFFSET. If monotonic time is much
+		 * shorter, an external clock adjustment (NTP, VM sync)
+		 * interfered with the test.
+		 */
+		if (mono_after.tv_sec - mono_before.tv_sec <
+		    SMALLTIME + SLEEPOFFSET - ACCEPTABLEDELTA - 1) {
+			printf("UNTESTED: external clock adjustment detected "
+			       "(monotonic elapsed %ds, expected ~%ds)\n",
+			       (int)(mono_after.tv_sec - mono_before.tv_sec),
+			       SMALLTIME + SLEEPOFFSET);
+			return PTS_UNTESTED;
+		}
+
 		if (WIFEXITED(i) && WEXITSTATUS(i)) {
 			printf("Test PASSED\n");
 			return PTS_PASS;
diff --git a/testcases/open_posix_testsuite/conformance/interfaces/clock_settime/7-2.c b/testcases/open_posix_testsuite/conformance/interfaces/clock_settime/7-2.c
index 7c340b6c97ce03a1b04709f06996e5a66e1a653a..1eced660640881e559e8b739e1a59df8897b17d4 100644
--- a/testcases/open_posix_testsuite/conformance/interfaces/clock_settime/7-2.c
+++ b/testcases/open_posix_testsuite/conformance/interfaces/clock_settime/7-2.c
@@ -31,6 +31,7 @@
 
 #define CHILDPASS 1
 #define CHILDFAIL 0
+#define CHILDUNTESTED 2
 
 int main(void)
 {
@@ -58,13 +59,38 @@ int main(void)
 		/* child here */
 		int flags = 0;
 		struct timespec tsT3;
+		struct timespec mono_start, mono_end;
 
 		flags |= TIMER_ABSTIME;
+
+		if (clock_gettime(CLOCK_MONOTONIC, &mono_start) != 0) {
+			perror("clock_gettime() was not successful\n");
+			return CHILDFAIL;
+		}
+
 		if (clock_nanosleep(CLOCK_REALTIME, flags, &tsT1, NULL) != 0) {
 			printf("clock_nanosleep() did not return success\n");
 			return CHILDFAIL;
 		}
 
+		if (clock_gettime(CLOCK_MONOTONIC, &mono_end) != 0) {
+			perror("clock_gettime() was not successful\n");
+			return CHILDFAIL;
+		}
+
+		/*
+		 * The parent sleeps 1s before jumping the clock forward.
+		 * If clock_nanosleep returned in less than 1s monotonic,
+		 * an external clock adjustment (NTP, VM sync) woke us
+		 * instead of the parent's clock_settime.
+		 */
+		if (mono_end.tv_sec - mono_start.tv_sec < 1) {
+			printf("UNTESTED: external clock adjustment detected "
+			       "(monotonic elapsed %ds)\n",
+			       (int)(mono_end.tv_sec - mono_start.tv_sec));
+			return CHILDUNTESTED;
+		}
+
 		if (clock_gettime(CLOCK_REALTIME, &tsT3) != 0) {
 			perror("clock_gettime() did not return success\n");
 			return CHILDFAIL;
@@ -105,7 +131,10 @@ int main(void)
 
 		setBackTime(tsreset);	//should be ~= before time
 
-		if (WIFEXITED(i) && WEXITSTATUS(i)) {
+		if (WIFEXITED(i) && WEXITSTATUS(i) == CHILDUNTESTED) {
+			printf("Test UNTESTED\n");
+			return PTS_UNTESTED;
+		} else if (WIFEXITED(i) && WEXITSTATUS(i)) {
 			printf("Test PASSED\n");
 			return PTS_PASS;
 		} else {

---
base-commit: 4688c20c01eece869b59e05ca3dd68c43e0d6af7
change-id: 20260331-clock_settime_fix-8e4117dcb609

Best regards,
-- 
Andrea Cervesato <andrea.cervesato@suse.com>


-- 
Mailing list info: https://lists.linux.it/listinfo/ltp

                 reply	other threads:[~2026-03-31 12:12 UTC|newest]

Thread overview: [no followups] expand[flat|nested]  mbox.gz  Atom feed

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=20260331-clock_settime_fix-v1-1-dfae06df2436@suse.com \
    --to=andrea.cervesato@suse.de \
    --cc=ltp@lists.linux.it \
    /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