lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-Id: <20250701-vdso-auxclock-v1-1-df7d9f87b9b8@linutronix.de>
Date: Tue, 01 Jul 2025 10:57:55 +0200
From: Thomas Weißschuh <thomas.weissschuh@...utronix.de>
To: Andy Lutomirski <luto@...nel.org>, Thomas Gleixner <tglx@...utronix.de>, 
 Vincenzo Frascino <vincenzo.frascino@....com>, 
 Shuah Khan <shuah@...nel.org>, 
 Anna-Maria Behnsen <anna-maria@...utronix.de>, 
 Frederic Weisbecker <frederic@...nel.org>, John Stultz <jstultz@...gle.com>, 
 Stephen Boyd <sboyd@...nel.org>, Catalin Marinas <catalin.marinas@....com>, 
 Will Deacon <will@...nel.org>, Arnd Bergmann <arnd@...db.de>
Cc: linux-kernel@...r.kernel.org, linux-kselftest@...r.kernel.org, 
 linux-arm-kernel@...ts.infradead.org, linux-arch@...r.kernel.org, 
 Richard Cochran <richardcochran@...il.com>, 
 Christopher Hall <christopher.s.hall@...el.com>, 
 Frederic Weisbecker <frederic@...nel.org>, 
 Anna-Maria Behnsen <anna-maria@...utronix.de>, 
 Miroslav Lichvar <mlichvar@...hat.com>, 
 Werner Abt <werner.abt@...nberg-usa.com>, 
 David Woodhouse <dwmw2@...radead.org>, Stephen Boyd <sboyd@...nel.org>, 
 Kurt Kanzenbach <kurt@...utronix.de>, Nam Cao <namcao@...utronix.de>, 
 Antoine Tenart <atenart@...nel.org>, 
 Thomas Weißschuh <thomas.weissschuh@...utronix.de>
Subject: [PATCH 01/14] selftests/timers: Add testcase for auxiliary clocks

Auxiliary clocks behave differently from regular ones.
Add a testcase to validate their functionality.

Signed-off-by: Thomas Weißschuh <thomas.weissschuh@...utronix.de>
---
 tools/testing/selftests/timers/.gitignore |   1 +
 tools/testing/selftests/timers/Makefile   |   2 +-
 tools/testing/selftests/timers/auxclock.c | 319 ++++++++++++++++++++++++++++++
 3 files changed, 321 insertions(+), 1 deletion(-)

diff --git a/tools/testing/selftests/timers/.gitignore b/tools/testing/selftests/timers/.gitignore
index bb5326ff900b8edc3aa2d8d596599973593fbaf0..dcee43b3ecd9351c9bb0483088d712ccd7b57367 100644
--- a/tools/testing/selftests/timers/.gitignore
+++ b/tools/testing/selftests/timers/.gitignore
@@ -20,3 +20,4 @@ valid-adjtimex
 adjtick
 set-tz
 freq-step
+auxclock
diff --git a/tools/testing/selftests/timers/Makefile b/tools/testing/selftests/timers/Makefile
index 32203593c62e1e0cdfd3de6f567ea1e82913f2ef..3a8833b3fb7449495c66a92c4d82e35a6755b5e8 100644
--- a/tools/testing/selftests/timers/Makefile
+++ b/tools/testing/selftests/timers/Makefile
@@ -5,7 +5,7 @@ LDLIBS += -lrt -lpthread -lm
 # these are all "safe" tests that don't modify
 # system time or require escalated privileges
 TEST_GEN_PROGS = posix_timers nanosleep nsleep-lat set-timer-lat mqueue-lat \
-	     inconsistency-check raw_skew threadtest rtcpie
+	     inconsistency-check raw_skew threadtest rtcpie auxclock
 
 DESTRUCTIVE_TESTS = alarmtimer-suspend valid-adjtimex adjtick change_skew \
 		      skew_consistency clocksource-switch freq-step leap-a-day \
diff --git a/tools/testing/selftests/timers/auxclock.c b/tools/testing/selftests/timers/auxclock.c
new file mode 100644
index 0000000000000000000000000000000000000000..0ba2f9996114ade3147f0f3aec49904556a23cd4
--- /dev/null
+++ b/tools/testing/selftests/timers/auxclock.c
@@ -0,0 +1,319 @@
+// SPDX-License-Identifier: GPL-2.0
+
+/* Work around type conflicts between libc and the UAPI headers */
+#define _SYS_TIME_H
+#define __timeval_defined
+#define _GNU_SOURCE
+
+#include <fcntl.h>
+#include <linux/types.h>
+#include <linux/timex.h>
+#include <sched.h>
+#include <stdio.h>
+#include <sys/syscall.h>
+#include <unistd.h>
+
+#include "../kselftest_harness.h"
+
+#ifndef CLOCK_AUX
+#define	CLOCK_AUX	16
+#endif
+
+#ifndef NSEC_PER_SEC
+#define NSEC_PER_SEC 1000000000ULL
+#endif
+
+#define AUXCLOCK_SELFTEST_TIMENS_OFFSET 10000
+
+static int configure_auxclock(__kernel_clockid_t clockid, bool enable)
+{
+	char path[100];
+	int fd, ret;
+
+	ret = snprintf(path, sizeof(path),
+		       "/sys/kernel/time/aux_clocks/%d/aux_clock_enable",
+		       (int)clockid - CLOCK_AUX);
+	if (ret >= sizeof(path))
+		return -ENOSPC;
+
+	fd = open(path, O_WRONLY);
+	if (fd == -1)
+		return -errno;
+
+	/* Always disable to reset */
+	ret = dprintf(fd, "0\n");
+	if (enable)
+		ret = dprintf(fd, "1\n");
+	close(fd);
+
+	if (ret < 0)
+		return ret;
+
+	return 0;
+}
+
+/* Everything is done in terms of 64bit time values to keep the code readable */
+
+static inline void timespec_to_kernel_timespec(const struct timespec *ts,
+					       struct __kernel_timespec *kts)
+{
+	if (!kts)
+		return;
+
+	kts->tv_sec = ts->tv_sec;
+	kts->tv_nsec = ts->tv_nsec;
+}
+
+static inline void kernel_timespec_to_timespec(const struct __kernel_timespec *kts,
+					       struct timespec *ts)
+{
+	if (!kts)
+		return;
+
+	ts->tv_sec = kts->tv_sec;
+	ts->tv_nsec = kts->tv_nsec;
+}
+
+static int sys_clock_getres_time64(__kernel_clockid_t clockid, struct __kernel_timespec *ts)
+{
+#if defined(__NR_clock_getres_time64)
+	return syscall(__NR_clock_getres_time64, clockid, ts);
+#elif defined(__NR_clock_getres)
+	struct timespec _ts;
+	int ret;
+
+	ret = syscall(__NR_clock_getres, clockid, &_ts);
+	if (!ret)
+		timespec_to_kernel_timespec(&_ts, ts);
+	return ret;
+#else
+#error "No clock_getres() support"
+#endif
+}
+
+static int sys_clock_gettime64(__kernel_clockid_t clockid, struct __kernel_timespec *ts)
+{
+#if defined(__NR_clock_gettime64)
+	return syscall(__NR_clock_gettime64, clockid, ts);
+#elif defined(__NR_clock_gettime)
+	struct timespec _ts;
+	int ret;
+
+	ret = syscall(__NR_clock_gettime, clockid, &_ts);
+	if (!ret)
+		timespec_to_kernel_timespec(&_ts, ts);
+	return ret;
+#else
+#error "No clock_gettime() support"
+#endif
+}
+
+static int sys_clock_settime64(__kernel_clockid_t clockid, const struct __kernel_timespec *ts)
+{
+#if defined(__NR_clock_settime64)
+	return syscall(__NR_clock_settime64, clockid, ts);
+#elif defined(__NR_clock_settime)
+	struct timespec _ts;
+
+	kernel_timespec_to_timespec(ts, &_ts);
+	return syscall(__NR_clock_settime, clockid, &_ts);
+#else
+#error "No clock_settime() support"
+#endif
+}
+
+static int sys_clock_adjtime64(__kernel_clockid_t clockid, struct __kernel_timex *tx)
+{
+#if defined(__NR_clock_adjtime64)
+	return syscall(__NR_clock_adjtime64, clockid, tx);
+#elif __LONG_WIDTH__ == 64 && defined(__NR_clock_adjtime)
+	return syscall(__NR_clock_adjtime, clockid, tx);
+#else
+#error "No clock_adjtime() support"
+#endif
+}
+
+FIXTURE(auxclock) {};
+
+FIXTURE_VARIANT(auxclock) {
+	__kernel_clockid_t clock;
+	bool clock_enabled;
+	bool use_timens;
+};
+
+FIXTURE_VARIANT_ADD(auxclock, default) {
+	.clock		= CLOCK_AUX,
+	.clock_enabled	= true,
+	.use_timens	= false,
+};
+
+FIXTURE_VARIANT_ADD(auxclock, timens) {
+	.clock		= CLOCK_AUX,
+	.clock_enabled	= true,
+	.use_timens	= true,
+};
+
+FIXTURE_VARIANT_ADD(auxclock, disabled) {
+	.clock		= CLOCK_AUX,
+	.clock_enabled	= false,
+	.use_timens	= false,
+};
+
+/* No timens_disabled to keep the testmatrix smaller. */
+
+static void enter_timens(struct __test_metadata *_metadata)
+{
+	int ret, fd;
+	char buf[100];
+
+	ret = unshare(CLONE_NEWTIME);
+	if (ret != 0 && errno == EPERM)
+		SKIP(return, "no permissions for unshare(CLONE_NEWTIME)");
+	if (ret != 0 && errno == EINVAL)
+		SKIP(return, "time namespaces not available");
+	ASSERT_EQ(0, ret) TH_LOG("unshare(CLONE_NEWTIME) failed: %s", strerror(errno));
+	fd = open("/proc/self/timens_offsets", O_WRONLY);
+	if (fd == -1 && errno == ENOENT)
+		SKIP(return, "no support for time namespaces");
+	ASSERT_NE(-1, fd);
+	/* Fiddle with the namespace to make the tests more meaningful */
+	ret = snprintf(buf, sizeof(buf), "monotonic %d 0\nboottime %d 0\n",
+		       AUXCLOCK_SELFTEST_TIMENS_OFFSET, AUXCLOCK_SELFTEST_TIMENS_OFFSET);
+	ASSERT_TRUE(ret > 0 && ret < sizeof(buf));
+	ret = write(fd, buf, ret);
+	ASSERT_NE(-1, ret);
+	close(fd);
+	fd = open("/proc/self/ns/time_for_children", O_RDONLY);
+	ASSERT_NE(-1, fd);
+	ret = setns(fd, CLONE_NEWTIME);
+	close(fd);
+	ASSERT_EQ(0, ret);
+}
+
+FIXTURE_SETUP(auxclock) {
+	int ret;
+
+	ret = configure_auxclock(variant->clock, variant->clock_enabled);
+	if (ret == -ENOENT)
+		SKIP(return, "auxclocks not enabled");
+	ASSERT_EQ(0, ret);
+
+	if (variant->use_timens)
+		enter_timens(_metadata);
+}
+
+FIXTURE_TEARDOWN(auxclock) {
+	int ret;
+
+	ret = configure_auxclock(variant->clock, false);
+	ASSERT_EQ(0, ret);
+}
+
+TEST_F(auxclock, sys_clock_getres) {
+	struct __kernel_timespec ts;
+	int ret;
+
+	/* clock_getres() is always expected to work */
+	ret = sys_clock_getres_time64(variant->clock, &ts);
+	ASSERT_EQ(0, ret);
+	ASSERT_EQ(0, ts.tv_sec);
+	ASSERT_EQ(1, ts.tv_nsec);
+}
+
+TEST_F(auxclock, sys_clock_gettime) {
+	struct __kernel_timespec ts;
+	int ret;
+
+	ret = sys_clock_gettime64(variant->clock, &ts);
+	if (variant->clock_enabled) {
+		ASSERT_EQ(0, ret);
+	} else {
+		ASSERT_EQ(-1, ret);
+		ASSERT_EQ(ENODEV, errno);
+	}
+}
+
+static void auxclock_validate_progression(struct __test_metadata *_metadata,
+					  const struct __kernel_timespec *a,
+					  const struct __kernel_timespec *b)
+{
+	int64_t diff;
+
+	diff = (b->tv_sec - a->tv_sec) * NSEC_PER_SEC;
+	diff += b->tv_nsec - a->tv_nsec;
+
+	/* Arbitrary values */
+	ASSERT_LT(1, diff);
+	ASSERT_GT(1 * NSEC_PER_SEC, diff);
+}
+
+TEST_F(auxclock, sys_clock_settime) {
+	struct __kernel_timespec a, b = {};
+	int ret;
+
+	a.tv_sec = 1234;
+	a.tv_nsec = 5678;
+
+	ret = sys_clock_settime64(variant->clock, &a);
+	if (!variant->clock_enabled) {
+		ASSERT_EQ(-1, ret);
+		ASSERT_EQ(ENODEV, errno);
+		return;
+	}
+
+	ASSERT_EQ(0, ret);
+
+	ret = sys_clock_gettime64(variant->clock, &b);
+	ASSERT_EQ(0, ret);
+
+	auxclock_validate_progression(_metadata, &a, &b);
+}
+
+TEST_F(auxclock, sys_clock_adjtime) {
+	struct __kernel_timex tx;
+	int ret, realtime_freq;
+
+	memset(&tx, 0, sizeof(tx));
+	tx.modes = ADJ_FREQUENCY;
+	ret = sys_clock_adjtime64(CLOCK_REALTIME, &tx);
+	ASSERT_NE(-1, ret);
+	ASSERT_TRUE(tx.modes & ADJ_FREQUENCY);
+	realtime_freq = tx.freq;
+
+	ret = sys_clock_adjtime64(variant->clock, &tx);
+	if (variant->clock_enabled) {
+		ASSERT_NE(-1, ret);
+		ASSERT_EQ(realtime_freq, tx.freq);
+	} else {
+		ASSERT_EQ(-1, ret);
+		ASSERT_EQ(ENODEV, errno);
+	}
+}
+
+TEST_F(auxclock, progression) {
+	struct __kernel_timespec a, b;
+	int ret;
+
+	if (!variant->clock_enabled) {
+		TH_LOG("no progression on disabled clocks");
+		return;
+	}
+
+	/* set up reference */
+	ret = sys_clock_gettime64(variant->clock, &a);
+	ASSERT_EQ(0, ret);
+
+	for (int i = 0; i < 100; i++) {
+		memset(&b, 0, sizeof(b));
+		ret = sys_clock_gettime64(variant->clock, &b);
+		ASSERT_EQ(0, ret);
+		auxclock_validate_progression(_metadata, &a, &b);
+
+		memset(&a, 0, sizeof(a));
+		ret = sys_clock_gettime64(variant->clock, &a);
+		ASSERT_EQ(0, ret);
+		auxclock_validate_progression(_metadata, &b, &a);
+	}
+}
+
+TEST_HARNESS_MAIN

-- 
2.50.0


Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ