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: 3+ 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
2026-04-29 7:50 ` Petr Vorel
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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.