From: Andrea Cervesato <andrea.cervesato@suse.de>
To: Linux Test Project <ltp@lists.linux.it>
Cc: Claude <noreply@anthropic.com>
Subject: [LTP] [PATCH v2] clock_settime: Detect external clock adjustments via CLOCK_MONOTONIC
Date: Tue, 31 Mar 2026 14:37:03 +0200 [thread overview]
Message-ID: <20260331-clock_settime_fix-v2-1-e222fe379b16@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. Guard the checks with _POSIX_MONOTONIC_CLOCK since
CLOCK_MONOTONIC is optional in POSIX.1-2001.
Co-authored-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 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 | 36 ++++++++++++++++++
.../conformance/interfaces/clock_settime/5-1.c | 43 +++++++++++++++++++++-
.../conformance/interfaces/clock_settime/5-2.c | 43 +++++++++++++++++++++-
.../conformance/interfaces/clock_settime/7-1.c | 38 +++++++++++++++++++
.../conformance/interfaces/clock_settime/7-2.c | 41 ++++++++++++++++++++-
5 files changed, 198 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..5f3f816a5fc71a9e62bb3577e1a18a19f987a802 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,9 @@ int main(void)
{
struct sigevent ev;
struct timespec tpT0, tpT2, tpreset;
+#ifdef _POSIX_MONOTONIC_CLOCK
+ struct timespec mono_before, mono_after;
+#endif
struct itimerspec its;
timer_t tid;
int delta;
@@ -55,6 +58,10 @@ int main(void)
return PTS_UNTESTED;
}
+#ifndef _POSIX_MONOTONIC_CLOCK
+ printf("CLOCK_MONOTONIC unavailable, test may fail due to external clock adjustments\n");
+#endif
+
/*
* set up sigevent for timer
* set up signal set for sigwait
@@ -82,6 +89,13 @@ int main(void)
return PTS_UNRESOLVED;
}
+#ifdef _POSIX_MONOTONIC_CLOCK
+ if (clock_gettime(CLOCK_MONOTONIC, &mono_before) != 0) {
+ perror("clock_gettime() was not successful\n");
+ return PTS_UNRESOLVED;
+ }
+#endif
+
if (timer_create(CLOCK_REALTIME, &ev, &tid) != 0) {
perror("timer_create() did not return success\n");
return PTS_UNRESOLVED;
@@ -120,6 +134,28 @@ int main(void)
tpreset.tv_sec += tpT2.tv_sec - tpT0.tv_sec;
setBackTime(tpreset);
+#ifdef _POSIX_MONOTONIC_CLOCK
+ if (clock_gettime(CLOCK_MONOTONIC, &mono_after) != 0) {
+ perror("clock_gettime() was not successful\n");
+ return PTS_UNRESOLVED;
+ }
+
+ /*
+ * 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;
+ }
+#endif
+
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..da8cbae29fd77fcbca939ac215f5c4d6d450fe31 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,13 @@ int main(void)
struct sigevent ev;
struct sigaction act;
struct timespec tsclock, ts, tsleft, tsreset;
+#ifdef _POSIX_MONOTONIC_CLOCK
+ struct timespec mono_before, mono_after;
+#endif
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) {
@@ -56,6 +60,10 @@ int main(void)
return PTS_UNTESTED;
}
+#ifndef _POSIX_MONOTONIC_CLOCK
+ printf("CLOCK_MONOTONIC unavailable, test may fail due to external clock adjustments\n");
+#endif
+
/*
* set up sigevent for timer
* set up signal set for sigwait
@@ -97,6 +105,13 @@ int main(void)
return PTS_UNRESOLVED;
}
+#ifdef _POSIX_MONOTONIC_CLOCK
+ if (clock_gettime(CLOCK_MONOTONIC, &mono_before) != 0) {
+ perror("clock_gettime() was not successful\n");
+ return PTS_UNRESOLVED;
+ }
+#endif
+
if (clock_gettime(CLOCK_REALTIME, &tsclock) != 0) {
printf("clock_gettime() did not return success\n");
return PTS_UNRESOLVED;
@@ -112,7 +127,33 @@ int main(void)
ts.tv_sec = TIMERSEC + SLEEPDELTA;
ts.tv_nsec = 0;
- if (nanosleep(&ts, &tsleft) != -1) {
+ ns_ret = nanosleep(&ts, &tsleft);
+
+#ifdef _POSIX_MONOTONIC_CLOCK
+ 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;
+ }
+#endif
+
+ 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..d12d18861ca78b174b47bae69d6e4a1146393159 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,13 @@ int main(void)
struct sigevent ev;
struct sigaction act;
struct timespec tsclock, ts, tsleft, tsreset;
+#ifdef _POSIX_MONOTONIC_CLOCK
+ struct timespec mono_before, mono_after;
+#endif
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) {
@@ -56,6 +60,10 @@ int main(void)
return PTS_UNTESTED;
}
+#ifndef _POSIX_MONOTONIC_CLOCK
+ printf("CLOCK_MONOTONIC unavailable, test may fail due to external clock adjustments\n");
+#endif
+
/*
* set up sigevent for timer
* set up signal set for sigwait
@@ -97,6 +105,13 @@ int main(void)
return PTS_UNRESOLVED;
}
+#ifdef _POSIX_MONOTONIC_CLOCK
+ if (clock_gettime(CLOCK_MONOTONIC, &mono_before) != 0) {
+ perror("clock_gettime() was not successful\n");
+ return PTS_UNRESOLVED;
+ }
+#endif
+
if (clock_gettime(CLOCK_REALTIME, &tsclock) != 0) {
printf("clock_gettime() did not return success\n");
return PTS_UNRESOLVED;
@@ -112,7 +127,33 @@ int main(void)
ts.tv_sec = TIMERSEC + SLEEPDELTA;
ts.tv_nsec = 0;
- if (nanosleep(&ts, &tsleft) != -1) {
+ ns_ret = nanosleep(&ts, &tsleft);
+
+#ifdef _POSIX_MONOTONIC_CLOCK
+ 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;
+ }
+#endif
+
+ 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..d372d2d48af0a29133eb833979ae37d682768170 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,9 @@
int main(void)
{
struct timespec tsT0, tsT1;
+#ifdef _POSIX_MONOTONIC_CLOCK
+ struct timespec mono_before, mono_after;
+#endif
int pid;
/* Check that we're root...can't call clock_settime with CLOCK_REALTIME otherwise */
@@ -44,11 +47,22 @@ int main(void)
return PTS_UNTESTED;
}
+#ifndef _POSIX_MONOTONIC_CLOCK
+ printf("CLOCK_MONOTONIC unavailable, test may fail due to external clock adjustments\n");
+#endif
+
if (clock_gettime(CLOCK_REALTIME, &tsT0) != 0) {
perror("clock_gettime() did not return success\n");
return PTS_UNRESOLVED;
}
+#ifdef _POSIX_MONOTONIC_CLOCK
+ if (clock_gettime(CLOCK_MONOTONIC, &mono_before) != 0) {
+ perror("clock_gettime() was not successful\n");
+ return PTS_UNRESOLVED;
+ }
+#endif
+
if ((pid = fork()) == 0) {
/* child here */
int flags = 0;
@@ -103,6 +117,30 @@ int main(void)
getBeforeTime(&tsreset); // get current time
tsreset.tv_sec += SMALLTIME;
setBackTime(tsreset);
+
+#ifdef _POSIX_MONOTONIC_CLOCK
+ if (clock_gettime(CLOCK_MONOTONIC, &mono_after) != 0) {
+ perror("clock_gettime() was not successful\n");
+ return PTS_UNRESOLVED;
+ }
+
+ /*
+ * 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;
+ }
+#endif
+
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..b9f10bfd5dfc517f0372662b1b1247298d2bee32 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)
{
@@ -43,6 +44,10 @@ int main(void)
return PTS_UNTESTED;
}
+#ifndef _POSIX_MONOTONIC_CLOCK
+ printf("CLOCK_MONOTONIC unavailable, test may fail due to external clock adjustments\n");
+#endif
+
if (clock_gettime(CLOCK_REALTIME, &tsT0) != 0) {
perror("clock_gettime() did not return success\n");
return PTS_UNRESOLVED;
@@ -58,13 +63,44 @@ int main(void)
/* child here */
int flags = 0;
struct timespec tsT3;
+#ifdef _POSIX_MONOTONIC_CLOCK
+ struct timespec mono_start, mono_end;
+#endif
flags |= TIMER_ABSTIME;
+
+#ifdef _POSIX_MONOTONIC_CLOCK
+ if (clock_gettime(CLOCK_MONOTONIC, &mono_start) != 0) {
+ perror("clock_gettime() was not successful\n");
+ return CHILDFAIL;
+ }
+#endif
+
if (clock_nanosleep(CLOCK_REALTIME, flags, &tsT1, NULL) != 0) {
printf("clock_nanosleep() did not return success\n");
return CHILDFAIL;
}
+#ifdef _POSIX_MONOTONIC_CLOCK
+ 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;
+ }
+#endif
+
if (clock_gettime(CLOCK_REALTIME, &tsT3) != 0) {
perror("clock_gettime() did not return success\n");
return CHILDFAIL;
@@ -105,7 +141,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
next reply other threads:[~2026-03-31 12:37 UTC|newest]
Thread overview: 2+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-03-31 12:37 Andrea Cervesato [this message]
2026-03-31 14:14 ` [LTP] [PATCH v2] clock_settime: Detect external clock adjustments via CLOCK_MONOTONIC Cyril Hrubis
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-v2-1-e222fe379b16@suse.com \
--to=andrea.cervesato@suse.de \
--cc=ltp@lists.linux.it \
--cc=noreply@anthropic.com \
/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