public inbox for ltp@lists.linux.it
 help / color / mirror / Atom feed
* [LTP] [PATCH v4] clock_settime: Detect external clock adjustments via CLOCK_MONOTONIC
@ 2026-04-09  8:55 Andrea Cervesato
  0 siblings, 0 replies; only message in thread
From: Andrea Cervesato @ 2026-04-09  8:55 UTC (permalink / raw)
  To: Linux Test Project; +Cc: Claude

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 sporadic failures.

Add pts_mono_time_start()/pts_mono_time_check() helpers to detect
external clock interference using CLOCK_MONOTONIC. When interference
is detected, the test retries up to PTS_MONO_MAX_RETRIES times before
reporting UNTESTED. Guard with _POSIX_MONOTONIC_CLOCK since
CLOCK_MONOTONIC is optional in POSIX.1-2001.

Co-developed-by: Claude <noreply@anthropic.com>
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.
---
Changes in v4:
- handle return value for helpers functions
- Link to v3: https://lore.kernel.org/r/20260401-clock_settime_fix-v3-1-f2d303703bd7@suse.com

Changes in v3:
- cycle multiple times before failing the tests
	- add helpers functions for CLOCK_MONOTONIC checks
- Link to v2: https://lore.kernel.org/r/20260331-clock_settime_fix-v2-1-e222fe379b16@suse.com

Changes in v2:
- print a message if CLOCK_MONOTONIC is not available
- always verify that CLOCK_MONOTONIC is available
- Link to v1: https://lore.kernel.org/r/20260331-clock_settime_fix-v1-1-dfae06df2436@suse.com
---
 .../conformance/interfaces/clock_settime/4-1.c     |  86 +++++++++------
 .../conformance/interfaces/clock_settime/5-1.c     |  68 +++++++-----
 .../conformance/interfaces/clock_settime/5-2.c     |  58 ++++++----
 .../conformance/interfaces/clock_settime/7-1.c     |  98 ++++++++++-------
 .../conformance/interfaces/clock_settime/7-2.c     | 119 +++++++++++++--------
 .../conformance/interfaces/clock_settime/helpers.h |  55 ++++++++++
 6 files changed, 323 insertions(+), 161 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..763a68fc2b5b085c67597fafa859b0aa300fb54b 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
@@ -47,7 +47,7 @@ int main(void)
 	int delta;
 	int sig;
 	sigset_t set;
-	int flags = 0;
+	int attempt, ret;
 
 	/* Check that we're root...can't call clock_settime with CLOCK_REALTIME otherwise */
 	if (geteuid() != 0) {
@@ -77,49 +77,65 @@ int main(void)
 		return PTS_UNRESOLVED;
 	}
 
-	if (clock_gettime(CLOCK_REALTIME, &tpT0) != 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;
 	}
 
-	flags |= TIMER_ABSTIME;
-	its.it_interval.tv_sec = 0;
-	its.it_interval.tv_nsec = 0;
-	its.it_value.tv_sec = tpT0.tv_sec + TIMEROFFSET;
-	its.it_value.tv_nsec = tpT0.tv_nsec;
-	if (timer_settime(tid, flags, &its, NULL) != 0) {
-		perror("timer_settime() did not return success\n");
-		return PTS_UNRESOLVED;
-	}
-
-	sleep(SLEEPTIME);
-	getBeforeTime(&tpreset);
-	if (clock_settime(CLOCK_REALTIME, &tpT0) != 0) {
-		perror("clock_settime() was not successful");
-		return PTS_UNRESOLVED;
-	}
-
-	if (sigwait(&set, &sig) == -1) {
-		perror("sigwait() was not successful\n");
-		return PTS_UNRESOLVED;
+	for (attempt = 0; attempt < PTS_MONO_MAX_RETRIES; attempt++) {
+		if (clock_gettime(CLOCK_REALTIME, &tpT0) != 0) {
+			perror("clock_gettime() was not successful\n");
+			return PTS_UNRESOLVED;
+		}
+
+		if (pts_mono_time_start() != 0)
+			return PTS_UNRESOLVED;
+
+		its.it_interval.tv_sec = 0;
+		its.it_interval.tv_nsec = 0;
+		its.it_value.tv_sec = tpT0.tv_sec + TIMEROFFSET;
+		its.it_value.tv_nsec = tpT0.tv_nsec;
+		if (timer_settime(tid, TIMER_ABSTIME, &its, NULL) != 0) {
+			perror("timer_settime() did not return success\n");
+			return PTS_UNRESOLVED;
+		}
+
+		sleep(SLEEPTIME);
+		getBeforeTime(&tpreset);
+		if (clock_settime(CLOCK_REALTIME, &tpT0) != 0) {
+			perror("clock_settime() was not successful");
+			return PTS_UNRESOLVED;
+		}
+
+		if (sigwait(&set, &sig) == -1) {
+			perror("sigwait() was not successful\n");
+			return PTS_UNRESOLVED;
+		}
+
+		if (clock_gettime(CLOCK_REALTIME, &tpT2) != 0) {
+			printf("clock_gettime() was not successful\n");
+			return PTS_UNRESOLVED;
+		}
+
+		delta = tpT2.tv_sec - its.it_value.tv_sec;
+
+		// add back time waited to reset value and reset time
+		tpreset.tv_sec += tpT2.tv_sec - tpT0.tv_sec;
+		setBackTime(tpreset);
+
+		ret = pts_mono_time_check(SLEEPTIME + TIMEROFFSET);
+		if (ret < 0)
+			return PTS_UNRESOLVED;
+		if (ret == 0)
+			break;
 	}
 
-	if (clock_gettime(CLOCK_REALTIME, &tpT2) != 0) {
-		printf("clock_gettime() was not successful\n");
-		return PTS_UNRESOLVED;
+	if (attempt == PTS_MONO_MAX_RETRIES) {
+		printf("UNTESTED: persistent clock interference after %d attempts\n",
+		       PTS_MONO_MAX_RETRIES);
+		return PTS_UNTESTED;
 	}
 
-	delta = tpT2.tv_sec - its.it_value.tv_sec;
-
-	// add back time waited to reset value and reset time
-	tpreset.tv_sec += tpT2.tv_sec - tpT0.tv_sec;
-	setBackTime(tpreset);
-
 	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..875cb7a80953d008e93beaa9aa4d88b94d676bec 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
@@ -49,6 +49,8 @@ int main(void)
 	struct itimerspec its;
 	timer_t tid;
 	sigset_t set;
+	int ns_ret;
+	int attempt, ret;
 
 	/* Check that we're root...can't call clock_settime with CLOCK_REALTIME otherwise */
 	if (getuid() != 0) {
@@ -92,42 +94,60 @@ int main(void)
 	its.it_value.tv_sec = TIMERSEC;
 	its.it_value.tv_nsec = 0;
 
-	if (timer_settime(tid, 0, &its, NULL) != 0) {
-		perror("timer_settime() did not return success\n");
-		return PTS_UNRESOLVED;
-	}
+	for (attempt = 0; attempt < PTS_MONO_MAX_RETRIES; attempt++) {
+		if (timer_settime(tid, 0, &its, NULL) != 0) {
+			perror("timer_settime() did not return success\n");
+			return PTS_UNRESOLVED;
+		}
 
-	if (clock_gettime(CLOCK_REALTIME, &tsclock) != 0) {
-		printf("clock_gettime() did not return success\n");
-		return PTS_UNRESOLVED;
-	}
+		if (pts_mono_time_start() != 0)
+			return PTS_UNRESOLVED;
 
-	tsclock.tv_sec -= CLOCKOFFSET;
-	getBeforeTime(&tsreset);
-	if (clock_settime(CLOCK_REALTIME, &tsclock) != 0) {
-		printf("clock_settime() 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;
+		}
+
+		tsclock.tv_sec -= CLOCKOFFSET;
+		getBeforeTime(&tsreset);
+		if (clock_settime(CLOCK_REALTIME, &tsclock) != 0) {
+			printf("clock_settime() was not successful\n");
+			return PTS_UNRESOLVED;
+		}
+
+		ts.tv_sec = TIMERSEC + SLEEPDELTA;
+		ts.tv_nsec = 0;
+
+		ns_ret = nanosleep(&ts, &tsleft);
+
+		ret = pts_mono_time_check(TIMERSEC);
+		tsreset.tv_sec += TIMERSEC;
+		setBackTime(tsreset);
+
+		if (ret < 0)
+			return PTS_UNRESOLVED;
+		if (ret == 0)
+			break;
 	}
 
-	ts.tv_sec = TIMERSEC + SLEEPDELTA;
-	ts.tv_nsec = 0;
+	if (attempt == PTS_MONO_MAX_RETRIES) {
+		printf("UNTESTED: persistent clock interference after %d attempts\n",
+		       PTS_MONO_MAX_RETRIES);
+		return PTS_UNTESTED;
+	}
 
-	if (nanosleep(&ts, &tsleft) != -1) {
+	if (ns_ret != -1) {
 		printf("nanosleep() not interrupted\n");
 		return PTS_FAIL;
 	}
 
 	if (labs(tsleft.tv_sec - SLEEPDELTA) <= ACCEPTABLEDELTA) {
 		printf("Test PASSED\n");
-		tsreset.tv_sec += TIMERSEC;
-		setBackTime(tsreset);
 		return PTS_PASS;
-	} else {
-		printf("Timer did not last for correct amount of time\n");
-		printf("timer: %d != correct %d\n",
-		       (int)ts.tv_sec - (int)tsleft.tv_sec, TIMERSEC);
-		return PTS_FAIL;
 	}
 
-	return PTS_UNRESOLVED;
+	printf("Timer did not last for correct amount of time\n");
+	printf("timer: %d != correct %d\n",
+	       (int)ts.tv_sec - (int)tsleft.tv_sec, TIMERSEC);
+	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..81dbeeb361e956e6b64dc582b596685c9750cfea 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
@@ -49,6 +49,8 @@ int main(void)
 	struct itimerspec its;
 	timer_t tid;
 	sigset_t set;
+	int ns_ret;
+	int attempt, ret;
 
 	/* Check that we're root...can't call clock_settime with CLOCK_REALTIME otherwise */
 	if (getuid() != 0) {
@@ -92,35 +94,55 @@ int main(void)
 	its.it_value.tv_sec = TIMERSEC;
 	its.it_value.tv_nsec = 0;
 
-	if (timer_settime(tid, 0, &its, NULL) != 0) {
-		perror("timer_settime() did not return success\n");
-		return PTS_UNRESOLVED;
-	}
+	for (attempt = 0; attempt < PTS_MONO_MAX_RETRIES; attempt++) {
+		if (timer_settime(tid, 0, &its, NULL) != 0) {
+			perror("timer_settime() did not return success\n");
+			return PTS_UNRESOLVED;
+		}
 
-	if (clock_gettime(CLOCK_REALTIME, &tsclock) != 0) {
-		printf("clock_gettime() did not return success\n");
-		return PTS_UNRESOLVED;
-	}
+		if (pts_mono_time_start() != 0)
+			return PTS_UNRESOLVED;
 
-	tsclock.tv_sec += CLOCKOFFSET;
-	getBeforeTime(&tsreset);
-	if (clock_settime(CLOCK_REALTIME, &tsclock) != 0) {
-		printf("clock_settime() 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;
+		}
+
+		tsclock.tv_sec += CLOCKOFFSET;
+		getBeforeTime(&tsreset);
+		if (clock_settime(CLOCK_REALTIME, &tsclock) != 0) {
+			printf("clock_settime() was not successful\n");
+			return PTS_UNRESOLVED;
+		}
+
+		ts.tv_sec = TIMERSEC + SLEEPDELTA;
+		ts.tv_nsec = 0;
+
+		ns_ret = nanosleep(&ts, &tsleft);
+
+		ret = pts_mono_time_check(TIMERSEC);
+		tsreset.tv_sec += TIMERSEC;
+		setBackTime(tsreset);
+
+		if (ret < 0)
+			return PTS_UNRESOLVED;
+		if (ret == 0)
+			break;
 	}
 
-	ts.tv_sec = TIMERSEC + SLEEPDELTA;
-	ts.tv_nsec = 0;
+	if (attempt == PTS_MONO_MAX_RETRIES) {
+		printf("UNTESTED: persistent clock interference after %d attempts\n",
+		       PTS_MONO_MAX_RETRIES);
+		return PTS_UNTESTED;
+	}
 
-	if (nanosleep(&ts, &tsleft) != -1) {
+	if (ns_ret != -1) {
 		printf("nanosleep() not interrupted\n");
 		return PTS_FAIL;
 	}
 
 	if (labs(tsleft.tv_sec - SLEEPDELTA) <= ACCEPTABLEDELTA) {
 		printf("Test PASSED\n");
-		tsreset.tv_sec += TIMERSEC;
-		setBackTime(tsreset);
 		return PTS_PASS;
 	}
 
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..399caee5a44ef356090062030a5a36aa2ccac28e 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
@@ -35,8 +35,9 @@
 
 int main(void)
 {
-	struct timespec tsT0, tsT1;
-	int pid;
+	struct timespec tsT0, tsT1, tsreset;
+	int pid, child_status;
+	int attempt, ret;
 
 	/* Check that we're root...can't call clock_settime with CLOCK_REALTIME otherwise */
 	if (getuid() != 0) {
@@ -44,50 +45,54 @@ int main(void)
 		return PTS_UNTESTED;
 	}
 
-	if (clock_gettime(CLOCK_REALTIME, &tsT0) != 0) {
-		perror("clock_gettime() did not return success\n");
-		return PTS_UNRESOLVED;
-	}
-
-	if ((pid = fork()) == 0) {
-		/* child here */
-		int flags = 0;
-		struct timespec tsT2;
+	for (attempt = 0; attempt < PTS_MONO_MAX_RETRIES; attempt++) {
+		if (clock_gettime(CLOCK_REALTIME, &tsT0) != 0) {
+			perror("clock_gettime() did not return success\n");
+			return PTS_UNRESOLVED;
+		}
 
-		tsT1.tv_sec = tsT0.tv_sec + SLEEPOFFSET;
-		tsT1.tv_nsec = tsT0.tv_nsec;
+		if (pts_mono_time_start() != 0)
+			return PTS_UNRESOLVED;
 
-		flags |= TIMER_ABSTIME;
-		if (clock_nanosleep(CLOCK_REALTIME, flags, &tsT1, NULL) != 0) {
-			printf("clock_nanosleep() did not return success\n");
-			return CHILDFAIL;
+		pid = fork();
+		if (pid < 0) {
+			perror("fork() failed");
+			return PTS_UNRESOLVED;
 		}
+		if (pid == 0) {
+			/* child here */
+			int flags = 0;
+			struct timespec tsT2;
 
-		if (clock_gettime(CLOCK_REALTIME, &tsT2) != 0) {
-			perror("clock_gettime() did not return success\n");
-			return CHILDFAIL;
-		}
+			tsT1.tv_sec = tsT0.tv_sec + SLEEPOFFSET;
+			tsT1.tv_nsec = tsT0.tv_nsec;
+
+			flags |= TIMER_ABSTIME;
+			if (clock_nanosleep(CLOCK_REALTIME, flags, &tsT1, NULL) != 0) {
+				printf("clock_nanosleep() did not return success\n");
+				return CHILDFAIL;
+			}
+
+			if (clock_gettime(CLOCK_REALTIME, &tsT2) != 0) {
+				perror("clock_gettime() did not return success\n");
+				return CHILDFAIL;
+			}
+
+			if (tsT2.tv_sec >= tsT1.tv_sec) {
+				if ((tsT2.tv_sec - tsT1.tv_sec) <= ACCEPTABLEDELTA)
+					return CHILDPASS;
 
-		if (tsT2.tv_sec >= tsT1.tv_sec) {
-			if ((tsT2.tv_sec - tsT1.tv_sec) <= ACCEPTABLEDELTA) {
-				return CHILDPASS;
-			} else {
 				printf("Ended too late.  %d >> %d\n",
 				       (int)tsT2.tv_sec, (int)tsT1.tv_sec);
 				return CHILDFAIL;
 			}
-		} else {
+
 			printf("Did not sleep for long enough %d < %d\n",
 			       (int)tsT2.tv_sec, (int)tsT1.tv_sec);
 			return CHILDFAIL;
 		}
 
-		return CHILDFAIL;
-	} else {
 		/* parent here */
-		int i;
-		struct timespec tsreset;
-
 		sleep(SMALLTIME);
 
 		if (clock_settime(CLOCK_REALTIME, &tsT0) != 0) {
@@ -95,22 +100,33 @@ int main(void)
 			return PTS_UNRESOLVED;
 		}
 
-		if (wait(&i) == -1) {
+		if (wait(&child_status) == -1) {
 			perror("Error waiting for child to exit\n");
 			return PTS_UNRESOLVED;
 		}
 
-		getBeforeTime(&tsreset);	// get current time
+		getBeforeTime(&tsreset);
 		tsreset.tv_sec += SMALLTIME;
 		setBackTime(tsreset);
-		if (WIFEXITED(i) && WEXITSTATUS(i)) {
-			printf("Test PASSED\n");
-			return PTS_PASS;
-		} else {
-			printf("Test FAILED\n");
-			return PTS_FAIL;
-		}
+
+		ret = pts_mono_time_check(SMALLTIME + SLEEPOFFSET);
+		if (ret < 0)
+			return PTS_UNRESOLVED;
+		if (ret == 0)
+			break;
+	}
+
+	if (attempt == PTS_MONO_MAX_RETRIES) {
+		printf("UNTESTED: persistent clock interference after %d attempts\n",
+		       PTS_MONO_MAX_RETRIES);
+		return PTS_UNTESTED;
+	}
+
+	if (WIFEXITED(child_status) && WEXITSTATUS(child_status)) {
+		printf("Test PASSED\n");
+		return PTS_PASS;
 	}
 
-	return PTS_UNRESOLVED;
+	printf("Test FAILED\n");
+	return PTS_FAIL;
 }
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..9a3a1e8678c95aef267f39995c85a35407699c6a 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,11 +31,14 @@
 
 #define CHILDPASS 1
 #define CHILDFAIL 0
+#define CHILDUNTESTED 2
+#define CHILDUNRESOLVED 3
 
 int main(void)
 {
-	struct timespec tsT0, tsT1, tsT2;
-	int pid;
+	struct timespec tsT0, tsT1, tsT2, tsreset;
+	int pid, child_status;
+	int attempt, ret;
 
 	/* Check that we're root...can't call clock_settime with CLOCK_REALTIME otherwise */
 	if (getuid() != 0) {
@@ -43,53 +46,70 @@ int main(void)
 		return PTS_UNTESTED;
 	}
 
-	if (clock_gettime(CLOCK_REALTIME, &tsT0) != 0) {
-		perror("clock_gettime() did not return success\n");
-		return PTS_UNRESOLVED;
-	}
-
-	tsT1.tv_sec = tsT0.tv_sec + SLEEPOFFSET;
-	tsT1.tv_nsec = tsT0.tv_nsec;
+	for (attempt = 0; attempt < PTS_MONO_MAX_RETRIES; attempt++) {
+		if (clock_gettime(CLOCK_REALTIME, &tsT0) != 0) {
+			perror("clock_gettime() did not return success\n");
+			return PTS_UNRESOLVED;
+		}
 
-	tsT2.tv_sec = tsT1.tv_sec + SMALLTIME;
-	tsT2.tv_nsec = tsT1.tv_nsec;
+		tsT1.tv_sec = tsT0.tv_sec + SLEEPOFFSET;
+		tsT1.tv_nsec = tsT0.tv_nsec;
 
-	if ((pid = fork()) == 0) {
-		/* child here */
-		int flags = 0;
-		struct timespec tsT3;
+		tsT2.tv_sec = tsT1.tv_sec + SMALLTIME;
+		tsT2.tv_nsec = tsT1.tv_nsec;
 
-		flags |= TIMER_ABSTIME;
-		if (clock_nanosleep(CLOCK_REALTIME, flags, &tsT1, NULL) != 0) {
-			printf("clock_nanosleep() did not return success\n");
-			return CHILDFAIL;
+		pid = fork();
+		if (pid < 0) {
+			perror("fork() failed");
+			return PTS_UNRESOLVED;
 		}
+		if (pid == 0) {
+			/* child here */
+			int flags = 0;
+			struct timespec tsT3;
 
-		if (clock_gettime(CLOCK_REALTIME, &tsT3) != 0) {
-			perror("clock_gettime() did not return success\n");
-			return CHILDFAIL;
-		}
+			flags |= TIMER_ABSTIME;
+
+			if (pts_mono_time_start() != 0)
+				return CHILDUNRESOLVED;
+
+			if (clock_nanosleep(CLOCK_REALTIME, flags, &tsT1, NULL) != 0) {
+				printf("clock_nanosleep() did not return success\n");
+				return CHILDFAIL;
+			}
+
+			/*
+			 * The parent sleeps 1s before jumping the clock forward.
+			 * If clock_nanosleep returned too quickly, an external
+			 * clock adjustment (NTP, VM sync) woke us instead of the
+			 * parent's clock_settime.
+			 */
+			ret = pts_mono_time_check(1);
+			if (ret < 0)
+				return CHILDUNRESOLVED;
+			if (ret > 0)
+				return CHILDUNTESTED;
+
+			if (clock_gettime(CLOCK_REALTIME, &tsT3) != 0) {
+				perror("clock_gettime() did not return success\n");
+				return CHILDFAIL;
+			}
+
+			if (tsT3.tv_sec >= tsT2.tv_sec) {
+				if ((tsT3.tv_sec - tsT2.tv_sec) <= ACCEPTABLEDELTA)
+					return CHILDPASS;
 
-		if (tsT3.tv_sec >= tsT2.tv_sec) {
-			if ((tsT3.tv_sec - tsT2.tv_sec) <= ACCEPTABLEDELTA) {
-				return CHILDPASS;
-			} else {
 				printf("Ended too late.  %d >> %d\n",
 				       (int)tsT3.tv_sec, (int)tsT2.tv_sec);
 				return CHILDFAIL;
 			}
-		} else {
+
 			printf("Did not sleep for long enough %d < %d\n",
 			       (int)tsT3.tv_sec, (int)tsT2.tv_sec);
 			return CHILDFAIL;
 		}
 
-		return CHILDFAIL;
-	} else {
 		/* parent here */
-		int i;
-		struct timespec tsreset;
-
 		sleep(1);
 
 		getBeforeTime(&tsreset);
@@ -98,21 +118,34 @@ int main(void)
 			return PTS_UNRESOLVED;
 		}
 
-		if (wait(&i) == -1) {
+		if (wait(&child_status) == -1) {
 			perror("Error waiting for child to exit\n");
 			return PTS_UNRESOLVED;
 		}
 
-		setBackTime(tsreset);	//should be ~= before time
+		setBackTime(tsreset);
 
-		if (WIFEXITED(i) && WEXITSTATUS(i)) {
-			printf("Test PASSED\n");
-			return PTS_PASS;
-		} else {
-			printf("Test FAILED\n");
-			return PTS_FAIL;
-		}
+		if (!WIFEXITED(child_status))
+			break;
+
+		if (WEXITSTATUS(child_status) == CHILDUNRESOLVED)
+			return PTS_UNRESOLVED;
+
+		if (WEXITSTATUS(child_status) != CHILDUNTESTED)
+			break;
+	}
+
+	if (attempt == PTS_MONO_MAX_RETRIES) {
+		printf("UNTESTED: persistent clock interference after %d attempts\n",
+		       PTS_MONO_MAX_RETRIES);
+		return PTS_UNTESTED;
+	}
+
+	if (WIFEXITED(child_status) && WEXITSTATUS(child_status) == CHILDPASS) {
+		printf("Test PASSED\n");
+		return PTS_PASS;
 	}
 
-	return PTS_UNRESOLVED;
+	printf("Test FAILED\n");
+	return PTS_FAIL;
 }
diff --git a/testcases/open_posix_testsuite/conformance/interfaces/clock_settime/helpers.h b/testcases/open_posix_testsuite/conformance/interfaces/clock_settime/helpers.h
index 133a579afa893e90d896d68fc92a93e21ef84efc..37bf30926f039553e9e370751b7938ca5ea1d00a 100644
--- a/testcases/open_posix_testsuite/conformance/interfaces/clock_settime/helpers.h
+++ b/testcases/open_posix_testsuite/conformance/interfaces/clock_settime/helpers.h
@@ -12,6 +12,9 @@
  * in certain tests, they make use of some libraries already included
  * by those tests.
  */
+
+#include <stdlib.h>
+
 static int getBeforeTime(struct timespec *tpget)
 {
 	if (clock_gettime(CLOCK_REALTIME, tpget) != 0) {
@@ -32,3 +35,55 @@ static int setBackTime(struct timespec tpset)
 	return PTS_PASS;
 }
 
+#define PTS_MONO_MAX_RETRIES 3
+
+#ifdef _POSIX_MONOTONIC_CLOCK
+static struct timespec _pts_mono_start;
+
+static inline int pts_mono_time_start(void)
+{
+	if (clock_gettime(CLOCK_MONOTONIC, &_pts_mono_start) != 0) {
+		perror("clock_gettime(CLOCK_MONOTONIC) failed");
+		return -1;
+	}
+	return 0;
+}
+
+static inline int pts_mono_time_check(unsigned int expected_secs)
+{
+	struct timespec now;
+	long elapsed;
+
+	if (clock_gettime(CLOCK_MONOTONIC, &now) != 0) {
+		perror("clock_gettime(CLOCK_MONOTONIC) failed");
+		return -1;
+	}
+
+	elapsed = now.tv_sec - _pts_mono_start.tv_sec;
+
+	if (labs(elapsed - (long)expected_secs) > 1) {
+		printf("Clock adjustment detected (elapsed %lds, expected ~%us)\n",
+		       elapsed, expected_secs);
+		return 1;
+	}
+	return 0;
+}
+#else
+static inline int pts_mono_time_start(void)
+{
+	static int warned;
+
+	if (!warned) {
+		printf("CLOCK_MONOTONIC unavailable, test may fail due to clock adjustment\n");
+		warned = 1;
+	}
+	return 0;
+}
+
+static inline int pts_mono_time_check(unsigned int expected_secs)
+{
+	(void)expected_secs;
+	return 0;
+}
+#endif
+

---
base-commit: 1c46589251d6efa461d0e837540c990cd6dea188
change-id: 20260331-clock_settime_fix-8e4117dcb609

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


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

^ permalink raw reply related	[flat|nested] only message in thread

only message in thread, other threads:[~2026-04-09  8:56 UTC | newest]

Thread overview: (only message) (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-04-09  8:55 [LTP] [PATCH v4] clock_settime: Detect external clock adjustments via CLOCK_MONOTONIC Andrea Cervesato

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox